From 3b3c4b2f4a8fc120b2f9fc35c8945f59042079d0 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 27 Oct 2021 06:34:07 -0600 Subject: [PATCH 01/46] [maps] Use desaturated map tiles instead of bright map tiles by default (#116179) * [maps] default to desaturated tiles in light mode * fix jest and functional tests * eslint * update migrations version expect * review feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../source_descriptor_types.ts | 1 + .../set_ems_tms_default_modes.test.ts | 58 +++++++++++++++++++ .../migrations/set_ems_tms_default_modes.ts | 40 +++++++++++++ .../create_basemap_layer_descriptor.test.ts | 1 + .../sources/ems_tms_source/ems_tms_source.js | 11 ++-- .../ems_tms_source/ems_tms_source.test.js | 12 ++++ .../ems_tms_source/tile_service_select.tsx | 2 +- .../maps/server/embeddable_migrations.ts | 9 ++- .../sample_data/ecommerce_saved_objects.js | 1 + .../sample_data/flights_saved_objects.js | 1 + .../sample_data/web_logs_saved_objects.js | 1 + .../saved_objects/saved_object_migrations.js | 11 +++- .../test/functional/apps/maps/sample_data.js | 6 +- .../upgrade/apps/maps/maps_smoke_tests.ts | 6 +- 14 files changed, 147 insertions(+), 13 deletions(-) create mode 100644 x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.test.ts create mode 100644 x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.ts diff --git a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts index 285c4043e46c78..e1b5d3e8190b08 100644 --- a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts @@ -27,6 +27,7 @@ export type AbstractSourceDescriptor = { export type EMSTMSSourceDescriptor = AbstractSourceDescriptor & { // id: EMS TMS layer id. Used when !isAutoSelect isAutoSelect: boolean; + lightModeDefault: string; }; export type EMSFileSourceDescriptor = AbstractSourceDescriptor & { diff --git a/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.test.ts b/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.test.ts new file mode 100644 index 00000000000000..5aab4e24c8ba66 --- /dev/null +++ b/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setEmsTmsDefaultModes } from './set_ems_tms_default_modes'; + +describe('setEmsTmsDefaultModes', () => { + test('Should handle missing layerListJSON attribute', () => { + const attributes = { + title: 'my map', + }; + expect(setEmsTmsDefaultModes({ attributes })).toEqual({ + title: 'my map', + }); + }); + + test('Should add lightModeDefault to existing EMS_TMS source descriptors', () => { + const layerListJSON = JSON.stringify([ + { + sourceDescriptor: { + type: 'EMS_TMS', + }, + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(setEmsTmsDefaultModes({ attributes })).toEqual({ + title: 'my map', + layerListJSON: '[{"sourceDescriptor":{"type":"EMS_TMS","lightModeDefault":"road_map"}}]', + }); + }); + + // test edge case where sample data maps set lightModeDefault but still run migration + test('Should not change lightModeDefault if provided', () => { + const layerListJSON = JSON.stringify([ + { + sourceDescriptor: { + type: 'EMS_TMS', + lightModeDefault: 'road_map_desaturated', + }, + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(setEmsTmsDefaultModes({ attributes })).toEqual({ + title: 'my map', + layerListJSON: + '[{"sourceDescriptor":{"type":"EMS_TMS","lightModeDefault":"road_map_desaturated"}}]', + }); + }); +}); diff --git a/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.ts b/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.ts new file mode 100644 index 00000000000000..94dbd8741add7f --- /dev/null +++ b/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SOURCE_TYPES } from '../constants'; +import { LayerDescriptor, EMSTMSSourceDescriptor } from '../descriptor_types'; +import { MapSavedObjectAttributes } from '../map_saved_object_type'; + +// LightModeDefault added to EMSTMSSourceDescriptor in 8.0.0 +// to avoid changing auto selected light mode tiles for maps created < 8.0.0 +// < 8.0.0 did not specify defaults and used bright for light mode +// > 8.0.0 changed default light mode from bright to desaturated +export function setEmsTmsDefaultModes({ + attributes, +}: { + attributes: MapSavedObjectAttributes; +}): MapSavedObjectAttributes { + if (!attributes || !attributes.layerListJSON) { + return attributes; + } + + const layerList: LayerDescriptor[] = JSON.parse(attributes.layerListJSON); + layerList.forEach((layerDescriptor: LayerDescriptor) => { + if (layerDescriptor.sourceDescriptor?.type === SOURCE_TYPES.EMS_TMS) { + const sourceDescriptor = layerDescriptor.sourceDescriptor as EMSTMSSourceDescriptor; + // auto select bright tiles for EMS_TMS layers created before 8.0.0 + if (!sourceDescriptor.lightModeDefault) { + sourceDescriptor.lightModeDefault = 'road_map'; + } + } + }); + + return { + ...attributes, + layerListJSON: JSON.stringify(layerList), + }; +} diff --git a/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts b/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts index 59ff7e5e6616be..cf3d8705380043 100644 --- a/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts @@ -85,6 +85,7 @@ describe('EMS is enabled', () => { source: undefined, sourceDescriptor: { isAutoSelect: true, + lightModeDefault: 'road_map_desaturated', type: 'EMS_TMS', }, style: { type: 'TILE' }, diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js index fec10501209602..2acb0578f6152e 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js @@ -43,7 +43,11 @@ export class EMSTMSSource extends AbstractTMSSource { type: SOURCE_TYPES.EMS_TMS, id: descriptor.id, isAutoSelect: - typeof descriptor.isAutoSelect !== 'undefined' ? !!descriptor.isAutoSelect : false, + typeof descriptor.isAutoSelect !== 'undefined' ? descriptor.isAutoSelect : false, + lightModeDefault: + typeof descriptor.lightModeDefault !== 'undefined' + ? descriptor.lightModeDefault + : getEmsTileLayerId().desaturated, }; } @@ -144,12 +148,11 @@ export class EMSTMSSource extends AbstractTMSSource { } getTileLayerId() { - if (!this._descriptor.isAutoSelect) { + if (!this._descriptor.isAutoSelect && this._descriptor.id) { return this._descriptor.id; } - const emsTileLayerId = getEmsTileLayerId(); - return getIsDarkMode() ? emsTileLayerId.dark : emsTileLayerId.bright; + return getIsDarkMode() ? getEmsTileLayerId().dark : this._descriptor.lightModeDefault; } async getLicensedFeatures() { diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js index 8998e895f6541f..833e4b0fff95ec 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js @@ -5,6 +5,18 @@ * 2.0. */ +jest.mock('../../../kibana_services', () => { + return { + getEmsTileLayerId: () => { + return { + bright: 'road_map', + desaturated: 'road_map_desaturated', + dark: 'dark_map', + }; + }, + }; +}); + jest.mock('../../../util', () => { return { getEmsTmsServices: () => { diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.tsx b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.tsx index e4a6fed934b8d4..60c047b13bb7ad 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.tsx @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; import { getEmsTmsServices } from '../../../util'; import { getEmsUnavailableMessage } from '../../../components/ems_unavailable_message'; -export const AUTO_SELECT = 'auto_select'; +const AUTO_SELECT = 'auto_select'; export interface EmsTmsSourceConfig { id: string | null; diff --git a/x-pack/plugins/maps/server/embeddable_migrations.ts b/x-pack/plugins/maps/server/embeddable_migrations.ts index 2a53198d8d247a..a49e776d4fe02f 100644 --- a/x-pack/plugins/maps/server/embeddable_migrations.ts +++ b/x-pack/plugins/maps/server/embeddable_migrations.ts @@ -8,12 +8,13 @@ import type { SerializableRecord } from '@kbn/utility-types'; import { MapSavedObjectAttributes } from '../common/map_saved_object_type'; import { moveAttribution } from '../common/migrations/move_attribution'; +import { setEmsTmsDefaultModes } from '../common/migrations/set_ems_tms_default_modes'; /* * Embeddables such as Maps, Lens, and Visualize can be embedded by value or by reference on a dashboard. * To ensure that any migrations (>7.12) are run correctly in both cases, * the migration function must be registered as both a saved object migration and an embeddable migration - + * * This is the embeddable migration registry. */ export const embeddableMigrations = { @@ -23,4 +24,10 @@ export const embeddableMigrations = { attributes: moveAttribution(state as { attributes: MapSavedObjectAttributes }), } as SerializableRecord; }, + '8.0.0': (state: SerializableRecord) => { + return { + ...state, + attributes: setEmsTmsDefaultModes(state as { attributes: MapSavedObjectAttributes }), + } as SerializableRecord; + }, }; diff --git a/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js b/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js index a0ac4db734bd7e..e778b9e4162302 100644 --- a/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js +++ b/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js @@ -15,6 +15,7 @@ const layerList = [ sourceDescriptor: { type: 'EMS_TMS', isAutoSelect: true, + lightModeDefault: 'road_map_desaturated', }, visible: true, style: {}, diff --git a/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js b/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js index 4e9915623d7c7d..645eb0a90e5601 100644 --- a/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js +++ b/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js @@ -14,6 +14,7 @@ const layerList = [ sourceDescriptor: { type: 'EMS_TMS', isAutoSelect: true, + lightModeDefault: 'road_map_desaturated', }, visible: true, style: {}, diff --git a/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js b/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js index 984b9de7ac2b6c..5cc460160a676e 100644 --- a/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js +++ b/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js @@ -15,6 +15,7 @@ const layerList = [ sourceDescriptor: { type: 'EMS_TMS', isAutoSelect: true, + lightModeDefault: 'road_map_desaturated', }, visible: true, style: {}, diff --git a/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js b/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js index 8866ebb6b3de39..5fc15e89297143 100644 --- a/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js +++ b/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js @@ -17,12 +17,13 @@ import { removeBoundsFromSavedObject } from '../../common/migrations/remove_boun import { setDefaultAutoFitToBounds } from '../../common/migrations/set_default_auto_fit_to_bounds'; import { addTypeToTermJoin } from '../../common/migrations/add_type_to_termjoin'; import { moveAttribution } from '../../common/migrations/move_attribution'; +import { setEmsTmsDefaultModes } from '../../common/migrations/set_ems_tms_default_modes'; /* * Embeddables such as Maps, Lens, and Visualize can be embedded by value or by reference on a dashboard. * To ensure that any migrations (>7.12) are run correctly in both cases, * the migration function must be registered as both a saved object migration and an embeddable migration - + * * This is the saved object migration registry. */ export const savedObjectMigrations = { @@ -104,6 +105,14 @@ export const savedObjectMigrations = { '7.14.0': (doc) => { const attributes = moveAttribution(doc); + return { + ...doc, + attributes, + }; + }, + '8.0.0': (doc) => { + const attributes = setEmsTmsDefaultModes(doc); + return { ...doc, attributes, diff --git a/x-pack/test/functional/apps/maps/sample_data.js b/x-pack/test/functional/apps/maps/sample_data.js index a6e4500d30593b..483379b2f49144 100644 --- a/x-pack/test/functional/apps/maps/sample_data.js +++ b/x-pack/test/functional/apps/maps/sample_data.js @@ -108,7 +108,7 @@ export default function ({ getPageObjects, getService, updateBaselines }) { describe('ecommerce', () => { before(async () => { await PageObjects.maps.loadSavedMap('[eCommerce] Orders by Country'); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.maps.toggleLayerVisibility('United Kingdom'); await PageObjects.maps.toggleLayerVisibility('France'); await PageObjects.maps.toggleLayerVisibility('United States'); @@ -136,7 +136,7 @@ export default function ({ getPageObjects, getService, updateBaselines }) { describe('flights', () => { before(async () => { await PageObjects.maps.loadSavedMap('[Flights] Origin Time Delayed'); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.timePicker.setCommonlyUsedTime('sample_data range'); await PageObjects.maps.enterFullScreen(); await PageObjects.maps.closeLegend(); @@ -160,7 +160,7 @@ export default function ({ getPageObjects, getService, updateBaselines }) { describe('web logs', () => { before(async () => { await PageObjects.maps.loadSavedMap('[Logs] Total Requests and Bytes'); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.maps.toggleLayerVisibility('Total Requests by Destination'); await PageObjects.timePicker.setCommonlyUsedTime('sample_data range'); await PageObjects.maps.enterFullScreen(); diff --git a/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts b/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts index da296e5a4f60aa..fb8d8c6c59a9db 100644 --- a/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts +++ b/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts @@ -111,7 +111,7 @@ export default function ({ ); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.maps.waitForLayersToLoad(); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.maps.toggleLayerVisibility('United Kingdom'); await PageObjects.maps.toggleLayerVisibility('France'); await PageObjects.maps.toggleLayerVisibility('United States'); @@ -141,7 +141,7 @@ export default function ({ ); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.maps.waitForLayersToLoad(); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.timePicker.setCommonlyUsedTime('sample_data range'); await PageObjects.maps.enterFullScreen(); await PageObjects.maps.closeLegend(); @@ -167,7 +167,7 @@ export default function ({ ); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.maps.waitForLayersToLoad(); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.maps.toggleLayerVisibility('Total Requests by Destination'); await PageObjects.timePicker.setCommonlyUsedTime('sample_data range'); await PageObjects.maps.enterFullScreen(); From 0381326ede64a6d6c1c9e6eaaa955abdcf236cb6 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Wed, 27 Oct 2021 08:52:14 -0400 Subject: [PATCH 02/46] Retrieve search results on Relevance Tuning page whenever precision or query is changed (#116316) --- .../precision_slider.test.tsx | 6 ++--- .../precision_slider/precision_slider.tsx | 4 ++-- .../relevance_tuning_logic.test.ts | 23 +++++++++++++------ .../relevance_tuning_logic.ts | 23 +++++++++++-------- .../relevance_tuning_preview.test.tsx | 4 ++-- .../relevance_tuning_preview.tsx | 4 ++-- .../components/relevance_tuning/utils.ts | 2 +- 7 files changed, 40 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.test.tsx index 7b041e885dfefe..3be30b77bc2e41 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.test.tsx @@ -25,7 +25,7 @@ const MOCK_VALUES = { const MOCK_ACTIONS = { // RelevanceTuningLogic - updatePrecision: jest.fn(), + setPrecision: jest.fn(), }; describe('PrecisionSlider', () => { @@ -49,12 +49,12 @@ describe('PrecisionSlider', () => { expect(wrapper.find('[data-test-subj="PrecisionRange"]').prop('value')).toEqual(2); }); - it('calls updatePrecision on change', () => { + it('updates the precision on change', () => { wrapper .find('[data-test-subj="PrecisionRange"]') .simulate('change', { target: { value: 10 } }); - expect(MOCK_ACTIONS.updatePrecision).toHaveBeenCalledWith(10); + expect(MOCK_ACTIONS.setPrecision).toHaveBeenCalledWith(10); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx index 6fcfb28cb9e376..8e7a59c290ce2e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx @@ -33,7 +33,7 @@ export const PrecisionSlider: React.FC = () => { searchSettings: { precision }, } = useValues(RelevanceTuningLogic); - const { updatePrecision } = useActions(RelevanceTuningLogic); + const { setPrecision } = useActions(RelevanceTuningLogic); const stepDescription = STEP_DESCRIPTIONS[precision]; @@ -102,7 +102,7 @@ export const PrecisionSlider: React.FC = () => { data-test-subj="PrecisionRange" value={precision} onChange={(e) => { - updatePrecision(parseInt((e.target as HTMLInputElement).value, 10)); + setPrecision(parseInt((e.target as HTMLInputElement).value, 10)); }} min={1} max={11} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts index 7590633b7afef4..193c5dbe8ac24d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts @@ -228,10 +228,10 @@ describe('RelevanceTuningLogic', () => { }); }); - describe('updatePrecision', () => { + describe('setPrecision', () => { it('should set precision inside search settings and set unsavedChanges to true', () => { mount(); - RelevanceTuningLogic.actions.updatePrecision(9); + RelevanceTuningLogic.actions.setPrecision(9); expect(RelevanceTuningLogic.values).toEqual({ ...DEFAULT_VALUES, @@ -1007,15 +1007,24 @@ describe('RelevanceTuningLogic', () => { }); }); - describe('updateSearchValue', () => { - it('should update the query then update search results', () => { + describe('setSearchQuery', () => { + it('shoulds update search results', () => { + mount(); + jest.spyOn(RelevanceTuningLogic.actions, 'getSearchResults'); + + RelevanceTuningLogic.actions.setSearchQuery('foo'); + + expect(RelevanceTuningLogic.actions.getSearchResults).toHaveBeenCalled(); + }); + }); + + describe('setPrecision', () => { + it('shoulds update search results', () => { mount(); - jest.spyOn(RelevanceTuningLogic.actions, 'setSearchQuery'); jest.spyOn(RelevanceTuningLogic.actions, 'getSearchResults'); - RelevanceTuningLogic.actions.updateSearchValue('foo'); + RelevanceTuningLogic.actions.setPrecision(9); - expect(RelevanceTuningLogic.actions.setSearchQuery).toHaveBeenCalledWith('foo'); expect(RelevanceTuningLogic.actions.getSearchResults).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts index 02df792f515a29..c94de74b5ab22e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts @@ -88,8 +88,7 @@ interface RelevanceTuningActions { optionType: keyof Pick; value: string; }; - updatePrecision(precision: number): { precision: number }; - updateSearchValue(query: string): string; + setPrecision(precision: number): { precision: number }; } interface RelevanceTuningValues { @@ -144,8 +143,7 @@ export const RelevanceTuningLogic = kea< optionType, value, }), - updatePrecision: (precision) => ({ precision }), - updateSearchValue: (query) => query, + setPrecision: (precision) => ({ precision }), }), reducers: () => ({ searchSettings: [ @@ -158,7 +156,7 @@ export const RelevanceTuningLogic = kea< onInitializeRelevanceTuning: (_, { searchSettings }) => searchSettings, setSearchSettings: (_, { searchSettings }) => searchSettings, setSearchSettingsResponse: (_, { searchSettings }) => searchSettings, - updatePrecision: (currentSearchSettings, { precision }) => ({ + setPrecision: (currentSearchSettings, { precision }) => ({ ...currentSearchSettings, precision, }), @@ -191,7 +189,7 @@ export const RelevanceTuningLogic = kea< unsavedChanges: [ false, { - updatePrecision: () => true, + setPrecision: () => true, setSearchSettings: () => true, setSearchSettingsResponse: () => false, }, @@ -268,7 +266,11 @@ export const RelevanceTuningLogic = kea< const { engineName } = EngineLogic.values; const { http } = HttpLogic.values; - const { search_fields: searchFields, boosts } = removeBoostStateProps(values.searchSettings); + const { + search_fields: searchFields, + boosts, + precision, + } = removeBoostStateProps(values.searchSettings); const url = `/internal/app_search/engines/${engineName}/search`; actions.setResultsLoading(true); @@ -283,6 +285,7 @@ export const RelevanceTuningLogic = kea< body: JSON.stringify({ boosts: isEmpty(filteredBoosts) ? undefined : filteredBoosts, search_fields: isEmpty(searchFields) ? undefined : searchFields, + precision, }), }); @@ -472,8 +475,10 @@ export const RelevanceTuningLogic = kea< }, }); }, - updateSearchValue: (query) => { - actions.setSearchQuery(query); + setSearchQuery: () => { + actions.getSearchResults(); + }, + setPrecision: () => { actions.getSearchResults(); }, }), diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.test.tsx index b39faa60294367..32325c13efdaea 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.test.tsx @@ -22,7 +22,7 @@ describe('RelevanceTuningPreview', () => { const result3 = { id: { raw: 3 } }; const actions = { - updateSearchValue: jest.fn(), + setSearchQuery: jest.fn(), }; const values = { @@ -79,7 +79,7 @@ describe('RelevanceTuningPreview', () => { wrapper.find(EuiFieldSearch).simulate('change', { target: { value: 'some search text' } }); - expect(actions.updateSearchValue).toHaveBeenCalledWith('some search text'); + expect(actions.setSearchQuery).toHaveBeenCalledWith('some search text'); }); it('will show user a prompt to enter a query if they have not entered one', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.tsx index 4f3b20b419e802..3a36a3df2b4722 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.tsx @@ -44,7 +44,7 @@ const noResultsCallout = ( ); export const RelevanceTuningPreview: React.FC = () => { - const { updateSearchValue } = useActions(RelevanceTuningLogic); + const { setSearchQuery } = useActions(RelevanceTuningLogic); const { searchResults, schema } = useValues(RelevanceTuningLogic); const { engineName, isMetaEngine } = useValues(EngineLogic); @@ -59,7 +59,7 @@ export const RelevanceTuningPreview: React.FC = () => { updateSearchValue(e.target.value)} + onChange={(e) => setSearchQuery(e.target.value)} placeholder={i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.preview.searchPlaceholder', { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/utils.ts index be953f973ebf83..0edace74d0cb95 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/utils.ts @@ -16,7 +16,7 @@ export const filterIfTerm = (array: string[], filterTerm: string): string[] => { return filterTerm === '' ? array : array.filter((item) => item.includes(filterTerm)); }; -export const removeBoostStateProps = (searchSettings: SearchSettings) => { +export const removeBoostStateProps = (searchSettings: SearchSettings): SearchSettings => { const updatedSettings = cloneDeep(searchSettings); const { boosts } = updatedSettings; const keys = Object.keys(boosts); From 8998655fcba9f0116f97897261d7e1eea55fd537 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Wed, 27 Oct 2021 09:05:40 -0400 Subject: [PATCH 03/46] Fix driver to the policy details loading state (#116208) --- .../pages/policy/view/policy_details.tsx | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index 6b5d6f03aca286..6acc48c55c6e3a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -10,12 +10,7 @@ import { i18n } from '@kbn/i18n'; import { useLocation } from 'react-router-dom'; import { EuiCallOut, EuiLoadingSpinner, EuiPageTemplate } from '@elastic/eui'; import { usePolicyDetailsSelector } from './policy_hooks'; -import { - policyDetails, - agentStatusSummary, - isLoading, - apiError, -} from '../store/policy_details/selectors'; +import { policyDetails, agentStatusSummary, apiError } from '../store/policy_details/selectors'; import { AgentsSummary } from './agents_summary'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { PolicyTabs } from './tabs'; @@ -39,7 +34,6 @@ export const PolicyDetails = React.memo(() => { const { getAppUrl } = useAppUrl(); // Store values - const loading = usePolicyDetailsSelector(isLoading); const policyApiError = usePolicyDetailsSelector(apiError); const policyItem = usePolicyDetailsSelector(policyDetails); const policyAgentStatusSummary = usePolicyDetailsSelector(agentStatusSummary); @@ -90,24 +84,24 @@ export const PolicyDetails = React.memo(() => { ); const pageBody: React.ReactNode = useMemo(() => { - if (loading) { + if (policyApiError) { return ( - + + {policyApiError?.message} + ); } - if (policyApiError) { + if (!policyItem) { return ( - - {policyApiError?.message} - + ); } @@ -118,7 +112,7 @@ export const PolicyDetails = React.memo(() => { } return ; - }, [isTrustedAppsByPolicyEnabled, loading, policyApiError]); + }, [isTrustedAppsByPolicyEnabled, policyApiError, policyItem]); return ( Date: Wed, 27 Oct 2021 09:07:19 -0400 Subject: [PATCH 04/46] make `EndpointPrivileges` object immutable (#116299) --- .../user_privileges/endpoint/use_endpoint_privileges.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts index 36f02d22487fc4..448cb215941deb 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts @@ -9,6 +9,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { useCurrentUser, useHttp } from '../../../lib/kibana'; import { appRoutesService, CheckPermissionsResponse } from '../../../../../../fleet/common'; import { useLicense } from '../../../hooks/use_license'; +import { Immutable } from '../../../../../common/endpoint/types'; export interface EndpointPrivileges { loading: boolean; @@ -30,7 +31,7 @@ export interface EndpointPrivileges { * **NOTE:** Consider using `usePrivileges().endpointPrivileges` instead of this hook in order * to keep API calls to a minimum. */ -export const useEndpointPrivileges = (): EndpointPrivileges => { +export const useEndpointPrivileges = (): Immutable => { const http = useHttp(); const user = useCurrentUser(); const isMounted = useRef(true); @@ -66,7 +67,7 @@ export const useEndpointPrivileges = (): EndpointPrivileges => { }, [user?.roles]); const privileges = useMemo(() => { - const privilegeList: EndpointPrivileges = { + const privilegeList: EndpointPrivileges = Object.freeze({ loading: !fleetCheckDone || !user, canAccessFleet, canAccessEndpointManagement: canAccessFleet && isSuperUser, @@ -75,7 +76,7 @@ export const useEndpointPrivileges = (): EndpointPrivileges => { // FIXME: Remove usages of the property below /** @deprecated */ isPlatinumPlus: isPlatinumPlusLicense, - }; + }); return privilegeList; }, [canAccessFleet, fleetCheckDone, isSuperUser, user, isPlatinumPlusLicense]); From 34b22578d677eaac575ab14b5482b8bf689d5e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 27 Oct 2021 15:20:19 +0200 Subject: [PATCH 05/46] [Logs UI] [Metrics UI] Remove deprecated alias config entries (#115974) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../source_configuration.ts | 2 -- x-pack/plugins/infra/server/deprecations.ts | 2 -- .../infra/server/lib/sources/sources.ts | 21 +------------------ x-pack/plugins/infra/server/plugin.ts | 2 -- 4 files changed, 1 insertion(+), 26 deletions(-) diff --git a/x-pack/plugins/infra/common/source_configuration/source_configuration.ts b/x-pack/plugins/infra/common/source_configuration/source_configuration.ts index 436432e9f0caf6..257cccc86595cd 100644 --- a/x-pack/plugins/infra/common/source_configuration/source_configuration.ts +++ b/x-pack/plugins/infra/common/source_configuration/source_configuration.ts @@ -49,8 +49,6 @@ export const TimestampFromString = new rt.Type( export const sourceConfigurationConfigFilePropertiesRT = rt.type({ sources: rt.type({ default: rt.partial({ - logAlias: rt.string, // Cannot be deprecated until 8.0.0. Will be converted to an indexName reference. - metricAlias: rt.string, fields: rt.partial({ timestamp: rt.string, message: rt.array(rt.string), diff --git a/x-pack/plugins/infra/server/deprecations.ts b/x-pack/plugins/infra/server/deprecations.ts index 70131cd96d117c..4c2f5f6a9b3d14 100644 --- a/x-pack/plugins/infra/server/deprecations.ts +++ b/x-pack/plugins/infra/server/deprecations.ts @@ -179,8 +179,6 @@ export const configDeprecations: ConfigDeprecationProvider = ({ deprecate }) => return completeConfig; } ), - deprecate('sources.default.logAlias', '8.0.0'), - deprecate('sources.default.metricAlias', '8.0.0'), ]; export const getInfraDeprecationsFactory = diff --git a/x-pack/plugins/infra/server/lib/sources/sources.ts b/x-pack/plugins/infra/server/lib/sources/sources.ts index 45da4546ad3b82..4d2f0a8c4a1598 100644 --- a/x-pack/plugins/infra/server/lib/sources/sources.ts +++ b/x-pack/plugins/infra/server/lib/sources/sources.ts @@ -212,26 +212,7 @@ export class InfraSources { fold(constant({}), identity) ); - // NOTE: Legacy logAlias needs converting to a logIndices reference until we can remove - // config file sources in 8.0.0. - if (staticSourceConfiguration && staticSourceConfiguration.logAlias) { - const convertedStaticSourceConfiguration: InfraStaticSourceConfiguration & { - logAlias?: string; - } = { - ...staticSourceConfiguration, - logIndices: { - type: 'index_name', - indexName: staticSourceConfiguration.logAlias, - }, - }; - delete convertedStaticSourceConfiguration.logAlias; - return mergeSourceConfiguration( - defaultSourceConfiguration, - convertedStaticSourceConfiguration - ); - } else { - return mergeSourceConfiguration(defaultSourceConfiguration, staticSourceConfiguration); - } + return mergeSourceConfiguration(defaultSourceConfiguration, staticSourceConfiguration); } private async getSavedSourceConfiguration( diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index bf9c5a152058e4..247888fc2ae70b 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -51,8 +51,6 @@ export const config: PluginConfigDescriptor = { schema.object({ default: schema.maybe( schema.object({ - logAlias: schema.maybe(schema.string()), // NOTE / TODO: Should be deprecated in 8.0.0 - metricAlias: schema.maybe(schema.string()), fields: schema.maybe( schema.object({ timestamp: schema.maybe(schema.string()), From 76e65967fc573d51e0291f7664ecbb67a6f3442d Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 27 Oct 2021 14:27:59 +0100 Subject: [PATCH 06/46] skip flaky suite (#116414) --- test/examples/embeddables/dashboard.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/examples/embeddables/dashboard.ts b/test/examples/embeddables/dashboard.ts index b97905ca9ce6a6..5c255b136c666d 100644 --- a/test/examples/embeddables/dashboard.ts +++ b/test/examples/embeddables/dashboard.ts @@ -100,7 +100,8 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide const PageObjects = getPageObjects(['common', 'visChart']); const monacoEditor = getService('monacoEditor'); - describe('dashboard container', () => { + // FLAKY: https://github.com/elastic/kibana/issues/116414 + describe.skip('dashboard container', () => { before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); await esArchiver.loadIfNeeded( From bed54a9d455ae39a04785377b9b67f394a5611b5 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 27 Oct 2021 14:43:03 +0100 Subject: [PATCH 07/46] skip flaky suite (#109943) --- .../plugins/transform/public/app/hooks/use_index_data.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx b/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx index 9729cc8e62b1f9..7aae41cf2e769c 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx @@ -78,7 +78,8 @@ describe('Transform: useIndexData()', () => { }); }); -describe('Transform: with useIndexData()', () => { +// FLAKY: https://github.com/elastic/kibana/issues/109943 +describe.skip('Transform: with useIndexData()', () => { test('Minimal initialization, no cross cluster search warning.', async () => { // Arrange const indexPattern = { From 6fbda831b54633d095002b07fdbe3c949a9e3eee Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Wed, 27 Oct 2021 16:04:28 +0200 Subject: [PATCH 08/46] unskips exceptions tests (#116149) --- .../integration/exceptions/from_alert.spec.ts | 22 ++++++------------- .../integration/exceptions/from_rule.spec.ts | 22 ++++++------------- .../cypress/screens/alerts.ts | 2 ++ .../security_solution/cypress/tasks/alerts.ts | 2 +- 4 files changed, 17 insertions(+), 31 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts index 002aa0bbc2b1ea..84ad93fa089434 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts @@ -8,7 +8,7 @@ import { getException } from '../../objects/exception'; import { getNewRule } from '../../objects/rule'; -import { ALERTS_COUNT, NUMBER_OF_ALERTS } from '../../screens/alerts'; +import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../screens/alerts'; import { RULE_STATUS } from '../../screens/create_new_rule'; import { @@ -31,13 +31,12 @@ import { removeException, waitForTheRuleToBeExecuted, } from '../../tasks/rule_details'; -import { refreshPage } from '../../tasks/security_header'; import { ALERTS_URL } from '../../urls/navigation'; import { cleanKibana } from '../../tasks/common'; describe('From alert', () => { - const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1'; + const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; beforeEach(() => { cleanKibana(); @@ -53,7 +52,6 @@ describe('From alert', () => { activatesRule(); waitForTheRuleToBeExecuted(); waitForAlertsToPopulate(); - refreshPage(); cy.get(ALERTS_COUNT).should('exist'); cy.get(NUMBER_OF_ALERTS).should('have.text', NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS); @@ -64,36 +62,30 @@ describe('From alert', () => { esArchiverUnload('auditbeat_for_exceptions2'); }); - // TODO: Unskip the test when `https://github.com/elastic/kibana/issues/108244` it is fixed - it.skip('Creates an exception and deletes it', () => { + it('Creates an exception and deletes it', () => { addExceptionFromFirstAlert(); addsException(getException()); esArchiverLoad('auditbeat_for_exceptions2'); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', '0 alerts'); + cy.get(EMPTY_ALERT_TABLE).should('exist'); goToClosedAlerts(); - refreshPage(); cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); goToOpenedAlerts(); waitForTheRuleToBeExecuted(); - refreshPage(); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', '0 alerts'); + cy.get(EMPTY_ALERT_TABLE).should('exist'); goToExceptionsTab(); removeException(); goToAlertsTab(); waitForTheRuleToBeExecuted(); waitForAlertsToPopulate(); - refreshPage(); cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts index c4e1d882d1853d..ea6b6cf0186b49 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts @@ -8,7 +8,7 @@ import { getException } from '../../objects/exception'; import { getNewRule } from '../../objects/rule'; -import { ALERTS_COUNT, NUMBER_OF_ALERTS } from '../../screens/alerts'; +import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../screens/alerts'; import { RULE_STATUS } from '../../screens/create_new_rule'; import { @@ -54,7 +54,7 @@ describe('From rule', () => { refreshPage(); cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alert`); }); afterEach(() => { @@ -62,40 +62,32 @@ describe('From rule', () => { esArchiverUnload('auditbeat_for_exceptions2'); }); - // TODO: Unskip the test when `https://github.com/elastic/kibana/issues/108244` it is fixed - it.skip('Creates an exception and deletes it', () => { + it('Creates an exception and deletes it', () => { goToExceptionsTab(); addsExceptionFromRuleSettings(getException()); esArchiverLoad('auditbeat_for_exceptions2'); waitForTheRuleToBeExecuted(); goToAlertsTab(); - refreshPage(); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', '0 alerts'); + cy.get(EMPTY_ALERT_TABLE).should('exist'); goToClosedAlerts(); - refreshPage(); cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alert`); goToOpenedAlerts(); waitForTheRuleToBeExecuted(); - refreshPage(); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', '0 alerts'); + cy.get(EMPTY_ALERT_TABLE).should('exist'); goToExceptionsTab(); removeException(); - refreshPage(); goToAlertsTab(); waitForTheRuleToBeExecuted(); waitForAlertsToPopulate(); - refreshPage(); cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alert`); }); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/cypress/screens/alerts.ts index 01848f42078465..a46f65acaf971f 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts.ts @@ -34,6 +34,8 @@ export const CLOSE_SELECTED_ALERTS_BTN = '[data-test-subj="close-alert-status"]' export const CLOSED_ALERTS_FILTER_BTN = '[data-test-subj="closedAlerts"]'; +export const EMPTY_ALERT_TABLE = '[data-test-subj="tGridEmptyState"]'; + export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]'; export const ACKNOWLEDGED_ALERTS_FILTER_BTN = '[data-test-subj="acknowledgedAlerts"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts index 067c9957189b9e..220c0fbdaa4cf4 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts @@ -34,7 +34,7 @@ import { } from '../screens/alerts_details'; export const addExceptionFromFirstAlert = () => { - cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click(); + cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click({ force: true }); cy.get(ADD_EXCEPTION_BTN).click(); }; From 9330ea7d97ec13164652ef81b79b9d9ed1362201 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Wed, 27 Oct 2021 16:44:03 +0200 Subject: [PATCH 09/46] [Security Solution] Skips EQL test (#116456) --- .../detection_rules/event_correlation_rule.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts index 171d224cc32d35..10f556a11bf602 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts @@ -70,7 +70,7 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { ALERTS_URL } from '../../urls/navigation'; -describe('Detection rules, EQL', () => { +describe.skip('Detection rules, EQL', () => { const expectedUrls = getEqlRule().referenceUrls.join(''); const expectedFalsePositives = getEqlRule().falsePositivesExamples.join(''); const expectedTags = getEqlRule().tags.join(''); @@ -169,7 +169,7 @@ describe('Detection rules, EQL', () => { }); }); -describe('Detection rules, sequence EQL', () => { +describe.skip('Detection rules, sequence EQL', () => { const expectedNumberOfRules = 1; const expectedNumberOfSequenceAlerts = '1 alert'; From 4be1d8f4384dd6b66eebef9df273fb14c30ff153 Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Wed, 27 Oct 2021 08:46:53 -0600 Subject: [PATCH 10/46] [docs] Re-add `experimental` label to dashboard import/export API docs. (#116348) --- docs/api/dashboard-api.asciidoc | 2 +- docs/api/dashboard/export-dashboard.asciidoc | 2 +- docs/api/dashboard/import-dashboard.asciidoc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/dashboard-api.asciidoc b/docs/api/dashboard-api.asciidoc index e6f54dd9156ecb..94511c3154fe09 100644 --- a/docs/api/dashboard-api.asciidoc +++ b/docs/api/dashboard-api.asciidoc @@ -1,7 +1,7 @@ [[dashboard-api]] == Import and export dashboard APIs -deprecated::[7.15.0,Both of these APIs have been deprecated in favor of <> and <>.] +deprecated::[7.15.0,These experimental APIs have been deprecated in favor of <> and <>.] Import and export dashboards with the corresponding saved objects, such as visualizations, saved searches, and index patterns. diff --git a/docs/api/dashboard/export-dashboard.asciidoc b/docs/api/dashboard/export-dashboard.asciidoc index 3a20eff0a54d25..098ec976569bd8 100644 --- a/docs/api/dashboard/export-dashboard.asciidoc +++ b/docs/api/dashboard/export-dashboard.asciidoc @@ -6,7 +6,7 @@ deprecated::[7.15.0,Use <> instead.] -Export dashboards and corresponding saved objects. +experimental[] Export dashboards and corresponding saved objects. [[dashboard-api-export-request]] ==== Request diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc index e4817d6cb7ee9b..41eb47500c8d7a 100644 --- a/docs/api/dashboard/import-dashboard.asciidoc +++ b/docs/api/dashboard/import-dashboard.asciidoc @@ -6,7 +6,7 @@ deprecated::[7.15.0,Use <> instead.] -Import dashboards and corresponding saved objects. +experimental[] Import dashboards and corresponding saved objects. [[dashboard-api-import-request]] ==== Request From d6f9adf73adb54905c68e7fec82d0780a18ebad1 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 27 Oct 2021 16:29:41 +0000 Subject: [PATCH 11/46] skip flaky suite (#116463) --- .../index_pattern_field_editor_example.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts b/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts index 5744c8e64f5c11..fa4308ae72883a 100644 --- a/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts +++ b/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts @@ -12,7 +12,8 @@ import { PluginFunctionalProviderContext } from 'test/plugin_functional/services export default function ({ getService }: PluginFunctionalProviderContext) { const testSubjects = getService('testSubjects'); - describe('', () => { + // FAILING: https://github.com/elastic/kibana/issues/116463 + describe.skip('', () => { it('finds an index pattern', async () => { await testSubjects.existOrFail('indexPatternTitle'); }); From e0fe09607f0604d3d69c2876e9323b3d37ef2d12 Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Wed, 27 Oct 2021 19:11:08 +0200 Subject: [PATCH 12/46] Unskip migrations actions integration tests (#116285) --- .../actions/integration_tests/actions.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts b/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts index 3ca3a8505338bd..8cf49440f76b8b 100644 --- a/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts +++ b/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts @@ -55,8 +55,7 @@ const { startES } = kbnTestServer.createTestServers({ }); let esServer: kbnTestServer.TestElasticsearchUtils; -// Failing: See https://github.com/elastic/kibana/issues/113697 -describe.skip('migration actions', () => { +describe('migration actions', () => { let client: ElasticsearchClient; beforeAll(async () => { @@ -1158,7 +1157,7 @@ describe.skip('migration actions', () => { it('resolves left wait_for_task_completion_timeout when the task does not complete within the timeout', async () => { const res = (await pickupUpdatedMappings( client, - 'existing_index_with_docs' + '.kibana_1' )()) as Either.Right; const task = waitForPickupUpdatedMappingsTask({ @@ -1539,7 +1538,8 @@ describe.skip('migration actions', () => { } `); }); - it('resolves left request_entity_too_large_exception when the payload is too large', async () => { + // TODO: unskip after https://github.com/elastic/kibana/issues/116111 + it.skip('resolves left request_entity_too_large_exception when the payload is too large', async () => { const newDocs = new Array(10000).fill({ _source: { title: From 6b4d9dd1a9dc3720334ce4eaa7f6085f4bd7f656 Mon Sep 17 00:00:00 2001 From: Nick Partridge Date: Wed, 27 Oct 2021 12:22:35 -0500 Subject: [PATCH 13/46] [Charts] Multi-layer time axis (#115853) This commit introduces the multi-layer time axis in Discover, Lens, Visualize, TSVB. It adds visualization:useLegacyTimeAxis advanced settings under charts plugin to toggle legacy time axis. The new multi-layer time axis is introduced in @elastic/charts https://elastic.github.io/elastic-charts/?path=/story/area-chart--timeslip and was demoed as part of the Kibana Demo Days. It is the outcome of the research done in elastic/elastic-charts#1310 related to improving the time axis solving the following problems: - sparse time labels that can be far apart - unclear where time point is on the label (the middle) - difficult / tedious to read due to redundant information and small fonts - resolution is not explicit (is it hours of days or days themselves) --- docs/management/advanced-options.asciidoc | 3 + package.json | 2 +- src/plugins/charts/common/index.ts | 1 + src/plugins/charts/server/plugin.ts | 17 ++- .../main/components/chart/discover_chart.tsx | 2 - .../apps/main/components/chart/histogram.tsx | 55 ++++++++- .../server/collectors/management/schema.ts | 4 + .../server/collectors/management/types.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 6 + .../components/vis_types/timeseries/vis.js | 2 + .../visualizations/views/timeseries/index.js | 54 ++++++++- .../xy/public/components/xy_axis.tsx | 2 + .../vis_types/xy/public/config/get_axis.ts | 66 +++++++++-- .../vis_types/xy/public/config/get_config.ts | 38 +++++-- src/plugins/vis_types/xy/public/plugin.ts | 8 +- .../vis_types/xy/public/types/config.ts | 1 + .../vis_types/xy/public/vis_component.tsx | 6 +- .../vis_types/xy/public/vis_renderer.tsx | 9 +- .../apps/dashboard/dashboard_snapshots.ts | 4 +- test/functional/apps/visualize/_timelion.ts | 2 +- test/functional/config.js | 1 + .../__snapshots__/expression.test.tsx.snap | 7 ++ .../xy_visualization/expression.test.tsx | 1 + .../public/xy_visualization/expression.tsx | 104 +++++++++++++----- .../lens/public/xy_visualization/index.ts | 2 + yarn.lock | 14 ++- 26 files changed, 341 insertions(+), 71 deletions(-) diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 6bac5e7940dbb3..f2da704760f3b3 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -516,6 +516,9 @@ Enables the legacy charts library for timelion charts in Visualize. **This setting is deprecated and will not be supported as of 8.0.** Maps values to specific colors in charts using the *Compatibility* palette. +[[visualization-uselegacytimeaxis]]`visualization:useLegacyTimeAxis`:: +Enables the legacy time axis for charts in Lens, Discover, Visualize and TSVB + [[visualization-heatmap-maxbuckets]]`visualization:heatmap:maxBuckets`:: The maximum number of buckets a datasource can return. High numbers can have a negative impact on your browser rendering performance. diff --git a/package.json b/package.json index b2065ddc1d6088..3c42c077ae5b02 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "@elastic/apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace", "@elastic/apm-rum": "^5.9.1", "@elastic/apm-rum-react": "^1.3.1", - "@elastic/charts": "38.0.1", + "@elastic/charts": "38.1.0", "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.35", "@elastic/ems-client": "8.0.0", diff --git a/src/plugins/charts/common/index.ts b/src/plugins/charts/common/index.ts index 618466212f5bb7..d06dbc73e29b1e 100644 --- a/src/plugins/charts/common/index.ts +++ b/src/plugins/charts/common/index.ts @@ -7,6 +7,7 @@ */ export const COLOR_MAPPING_SETTING = 'visualization:colorMapping'; +export const LEGACY_TIME_AXIS = 'visualization:useLegacyTimeAxis'; export { CustomPaletteArguments, diff --git a/src/plugins/charts/server/plugin.ts b/src/plugins/charts/server/plugin.ts index 63b703e6b75384..c7559b525cd223 100644 --- a/src/plugins/charts/server/plugin.ts +++ b/src/plugins/charts/server/plugin.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { CoreSetup, Plugin } from 'kibana/server'; -import { COLOR_MAPPING_SETTING, palette, systemPalette } from '../common'; +import { COLOR_MAPPING_SETTING, LEGACY_TIME_AXIS, palette, systemPalette } from '../common'; import { ExpressionsServerSetup } from '../../expressions/server'; interface SetupDependencies { @@ -45,6 +45,21 @@ export class ChartsServerPlugin implements Plugin { category: ['visualization'], schema: schema.string(), }, + [LEGACY_TIME_AXIS]: { + name: i18n.translate('charts.advancedSettings.visualization.useLegacyTimeAxis.name', { + defaultMessage: 'Legacy chart time axis', + }), + value: false, + description: i18n.translate( + 'charts.advancedSettings.visualization.useLegacyTimeAxis.description', + { + defaultMessage: + 'Enables the legacy time axis for charts in Lens, Discover, Visualize and TSVB', + } + ), + category: ['visualization'], + schema: schema.boolean(), + }, }); return {}; diff --git a/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx b/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx index 166c2272a00f42..1fe149f3eb17dd 100644 --- a/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx +++ b/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx @@ -115,7 +115,6 @@ export function DiscoverChart({ onResetQuery={resetSavedSearch} /> - {showViewModeToggle && ( )} - {timefield && ( = useLegacyTimeAxis + ? {} + : { strokeWidth: 0.1, stroke: isDarkMode ? 'white' : 'black' }; + const verticalAxisStyle: RecursivePartial = useLegacyTimeAxis + ? {} + : { + axisLine: { + visible: false, + }, + tickLabel: { + fontSize: 11, + }, + }; + const xAxisStyle: RecursivePartial = useLegacyTimeAxis + ? {} + : { + axisLine: { + stroke: isDarkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: isDarkMode ? 'white' : 'black', + padding: -10, + visible: true, + }, + tickLabel: { + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + }; + return (
@@ -195,15 +241,19 @@ export function DiscoverHistogram({ xAxisFormatter.convert(value)} + gridLine={gridLineStyle} + style={verticalAxisStyle} /> diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 007d3a99cb1dd8..001efe0433dc8f 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -64,6 +64,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'text', _meta: { description: 'Non-default value of setting.' }, }, + 'visualization:useLegacyTimeAxis': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'visualization:regionmap:showWarnings': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index d35a05fe04780b..53d651b022529f 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -40,6 +40,7 @@ export interface UsageStats { 'visualize:enableLabs': boolean; 'visualization:heatmap:maxBuckets': number; 'visualization:colorMapping': string; + 'visualization:useLegacyTimeAxis': boolean; 'visualization:regionmap:showWarnings': boolean; 'visualization:tileMap:maxPrecision': number; 'csv:separator': string; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 138ce3f097ce96..e7c18966ce0c09 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7110,6 +7110,12 @@ "description": "Non-default value of setting." } }, + "visualization:useLegacyTimeAxis": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "visualization:regionmap:showWarnings": { "type": "boolean", "_meta": { diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js index b4fe39c522de76..b177ef632e2107 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js @@ -19,6 +19,7 @@ import { createFieldFormatter } from '../../lib/create_field_formatter'; import { checkIfSeriesHaveSameFormatters } from '../../lib/check_if_series_have_same_formatters'; import { TimeSeries } from '../../../visualizations/views/timeseries'; import { MarkdownSimple } from '../../../../../../../../plugins/kibana_react/public'; +import { LEGACY_TIME_AXIS } from '../../../../../../../../plugins/charts/common'; import { replaceVars } from '../../lib/replace_vars'; import { getInterval } from '../../lib/get_interval'; import { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter'; @@ -272,6 +273,7 @@ class TimeseriesVisualization extends Component { syncColors={syncColors} palettesService={palettesService} interval={interval} + useLegacyTimeAxis={getConfig(LEGACY_TIME_AXIS, false)} isLastBucketDropped={Boolean( model.drop_last_bucket || model.series.some((series) => series.series_drop_last_bucket) diff --git a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js index 2158283bb80d57..5e98b74c0caa5b 100644 --- a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js @@ -72,6 +72,7 @@ export const TimeSeries = ({ palettesService, interval, isLastBucketDropped, + useLegacyTimeAxis, }) => { // If the color isn't configured by the user, use the color mapping service // to assign a color from the Kibana palette. Colors will be shared across the @@ -138,6 +139,51 @@ export const TimeSeries = ({ }, [palettesService, series, syncColors] ); + + const darkMode = uiSettings.get('theme:darkMode'); + const gridLineStyle = !useLegacyTimeAxis + ? { + visible: showGrid, + strokeWidth: 0.1, + stroke: darkMode ? 'white' : 'black', + } + : { + ...GRID_LINE_CONFIG, + visible: showGrid, + }; + const xAxisStyle = !useLegacyTimeAxis + ? { + tickLabel: { + visible: true, + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: true, + }, + axisTitle: { + visible: true, + padding: 0, + }, + } + : {}; + return ( ); @@ -357,4 +402,5 @@ TimeSeries.propTypes = { annotations: PropTypes.array, interval: PropTypes.number, isLastBucketDropped: PropTypes.bool, + useLegacyTimeAxis: PropTypes.bool, }; diff --git a/src/plugins/vis_types/xy/public/components/xy_axis.tsx b/src/plugins/vis_types/xy/public/components/xy_axis.tsx index 30e1dbbff673ee..b224639bdbff3a 100644 --- a/src/plugins/vis_types/xy/public/components/xy_axis.tsx +++ b/src/plugins/vis_types/xy/public/components/xy_axis.tsx @@ -25,6 +25,7 @@ export const XYAxis: FC = ({ domain, style, integersOnly, + timeAxisLayerCount, }) => ( = ({ labelFormat={ticks?.labelFormatter} showOverlappingLabels={ticks?.showOverlappingLabels} showDuplicatedTicks={ticks?.showDuplicates} + timeAxisLayerCount={timeAxisLayerCount} /> ); diff --git a/src/plugins/vis_types/xy/public/config/get_axis.ts b/src/plugins/vis_types/xy/public/config/get_axis.ts index 0d6c67d064cf82..796636ef2cb61f 100644 --- a/src/plugins/vis_types/xy/public/config/get_axis.ts +++ b/src/plugins/vis_types/xy/public/config/get_axis.ts @@ -8,7 +8,13 @@ import { identity } from 'lodash'; -import { AxisSpec, TickFormatter, YDomainRange, ScaleType as ECScaleType } from '@elastic/charts'; +import { + AxisSpec, + TickFormatter, + YDomainRange, + ScaleType as ECScaleType, + Position, +} from '@elastic/charts'; import { LabelRotation } from '../../../../charts/public'; import { BUCKET_TYPES } from '../../../../data/public'; @@ -33,7 +39,9 @@ export function getAxis( { categoryLines, valueAxis }: Grid, { params, format, formatter, title: fallbackTitle = '', aggType }: Aspect, seriesParams: SeriesParam[], - isDateHistogram = false + isDateHistogram = false, + useMultiLayerTimeAxis = false, + darkMode = false ): AxisConfig { const isCategoryAxis = type === AxisType.Category; // Hide unassigned axis, not supported in elastic charts @@ -74,9 +82,10 @@ export function getAxis( ticks, grid, scale, - style: getAxisStyle(ticks, title, fallbackRotation), + style: getAxisStyle(useMultiLayerTimeAxis, darkMode, ticks, title, fallbackRotation), domain: getAxisDomain(scale, isCategoryAxis), integersOnly: aggType === 'count', + timeAxisLayerCount: useMultiLayerTimeAxis ? 3 : 0, }; } @@ -147,19 +156,52 @@ export function getScale( } function getAxisStyle( + isMultiLayerTimeAxis: boolean, + darkMode: boolean, ticks?: TickOptions, title?: string, rotationFallback: LabelRotation = LabelRotation.Vertical ): AxisSpec['style'] { - return { - axisTitle: { - visible: (title ?? '').trim().length > 0, - }, - tickLabel: { - visible: ticks?.show, - rotation: -(ticks?.rotation ?? rotationFallback), - }, - }; + return isMultiLayerTimeAxis + ? { + tickLabel: { + visible: Boolean(ticks?.show), + rotation: 0, // rotation is disabled on new time axis + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: Boolean(ticks?.show), + }, + axisTitle: { + visible: (title ?? '').trim().length > 0, + }, + } + : { + axisTitle: { + visible: (title ?? '').trim().length > 0, + }, + tickLabel: { + visible: ticks?.show, + rotation: -(ticks?.rotation ?? rotationFallback), + }, + }; } function getAxisDomain( diff --git a/src/plugins/vis_types/xy/public/config/get_config.ts b/src/plugins/vis_types/xy/public/config/get_config.ts index d2a3b9ad2a1037..bd79b915be9172 100644 --- a/src/plugins/vis_types/xy/public/config/get_config.ts +++ b/src/plugins/vis_types/xy/public/config/get_config.ts @@ -29,7 +29,12 @@ import { getAxis } from './get_axis'; import { getAspects } from './get_aspects'; import { ChartType } from '../index'; -export function getConfig(table: Datatable, params: VisParams): VisConfig { +export function getConfig( + table: Datatable, + params: VisParams, + useLegacyTimeAxis = false, + darkMode = false +): VisConfig { const { thresholdLine, orderBucketsBySum, @@ -42,13 +47,6 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { fillOpacity, } = params; const aspects = getAspects(table.columns, params.dimensions); - const xAxis = getAxis( - params.categoryAxes[0], - params.grid, - aspects.x, - params.seriesParams, - params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM - ); const tooltip = getTooltip(aspects, params); const yAxes = params.valueAxes.map((a) => { // find the correct aspect for each value axis @@ -60,10 +58,28 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { params.seriesParams ); }); + + const rotation = getRotation(params.categoryAxes[0]); + + const isDateHistogram = params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM; + const isHistogram = params.dimensions.x?.aggType === BUCKET_TYPES.HISTOGRAM; const enableHistogramMode = - (params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM || - params.dimensions.x?.aggType === BUCKET_TYPES.HISTOGRAM) && + (isDateHistogram || isHistogram) && shouldEnableHistogramMode(params.seriesParams, aspects.y, yAxes); + + const useMultiLayerTimeAxis = + enableHistogramMode && isDateHistogram && !useLegacyTimeAxis && rotation === 0; + + const xAxis = getAxis( + params.categoryAxes[0], + params.grid, + aspects.x, + params.seriesParams, + isDateHistogram, + useMultiLayerTimeAxis, + darkMode + ); + const isTimeChart = (aspects.x.params as DateHistogramParams).date ?? false; return { @@ -83,7 +99,7 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { xAxis, yAxes, legend: getLegend(params), - rotation: getRotation(params.categoryAxes[0]), + rotation, thresholdLine: getThresholdLine(thresholdLine, yAxes, params.seriesParams), }; } diff --git a/src/plugins/vis_types/xy/public/plugin.ts b/src/plugins/vis_types/xy/public/plugin.ts index c79ead242e35b0..0f1de387161e30 100644 --- a/src/plugins/vis_types/xy/public/plugin.ts +++ b/src/plugins/vis_types/xy/public/plugin.ts @@ -24,7 +24,7 @@ import { } from './services'; import { visTypesDefinitions } from './vis_types'; -import { xyVisRenderer } from './vis_renderer'; +import { getXYVisRenderer } from './vis_renderer'; import * as expressionFunctions from './expression_functions'; @@ -69,7 +69,11 @@ export class VisTypeXyPlugin setThemeService(charts.theme); setPalettesService(charts.palettes); - expressions.registerRenderer(xyVisRenderer); + expressions.registerRenderer( + getXYVisRenderer({ + uiSettings: core.uiSettings, + }) + ); expressions.registerFunction(expressionFunctions.visTypeXyVisFn); expressions.registerFunction(expressionFunctions.categoryAxis); expressions.registerFunction(expressionFunctions.timeMarker); diff --git a/src/plugins/vis_types/xy/public/types/config.ts b/src/plugins/vis_types/xy/public/types/config.ts index e52b47366bc857..287787193bd202 100644 --- a/src/plugins/vis_types/xy/public/types/config.ts +++ b/src/plugins/vis_types/xy/public/types/config.ts @@ -85,6 +85,7 @@ export interface AxisConfig { title?: string; grid?: AxisGrid; integersOnly: boolean; + timeAxisLayerCount?: number; } export interface LegendOptions { diff --git a/src/plugins/vis_types/xy/public/vis_component.tsx b/src/plugins/vis_types/xy/public/vis_component.tsx index 515ad3e7eaf6fa..8574e86a230962 100644 --- a/src/plugins/vis_types/xy/public/vis_component.tsx +++ b/src/plugins/vis_types/xy/public/vis_component.tsx @@ -66,6 +66,7 @@ export interface VisComponentProps { fireEvent: IInterpreterRenderHandlers['event']; renderComplete: IInterpreterRenderHandlers['done']; syncColors: boolean; + useLegacyTimeAxis: boolean; } export type VisComponentType = typeof VisComponent; @@ -211,8 +212,9 @@ const VisComponent = (props: VisComponentProps) => { ); const { visData, visParams, syncColors } = props; + const isDarkMode = getThemeService().useDarkMode(); - const config = getConfig(visData, visParams); + const config = getConfig(visData, visParams, props.useLegacyTimeAxis, isDarkMode); const timeZone = getTimeZone(); const xDomain = config.xAxis.scale.type === ScaleType.Ordinal ? undefined : getXDomain(config.aspects.x.params); @@ -229,7 +231,7 @@ const VisComponent = (props: VisComponentProps) => { () => config.legend.position ?? Position.Right, [config.legend.position] ); - const isDarkMode = getThemeService().useDarkMode(); + const getSeriesName = getSeriesNameFn(config.aspects, config.aspects.y.length > 1); const splitAccessors = config.aspects.series?.map(({ accessor, formatter }) => { diff --git a/src/plugins/vis_types/xy/public/vis_renderer.tsx b/src/plugins/vis_types/xy/public/vis_renderer.tsx index 093671307d5383..77727761015a74 100644 --- a/src/plugins/vis_types/xy/public/vis_renderer.tsx +++ b/src/plugins/vis_types/xy/public/vis_renderer.tsx @@ -9,6 +9,7 @@ import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; +import { IUiSettingsClient } from 'kibana/public'; import { VisualizationContainer } from '../../../visualizations/public'; import type { PersistedState } from '../../../visualizations/public'; @@ -17,6 +18,7 @@ import type { ExpressionRenderDefinition } from '../../../expressions/public'; import type { XyVisType } from '../common'; import type { VisComponentType } from './vis_component'; import { RenderValue, visName } from './expression_functions/xy_vis_fn'; +import { LEGACY_TIME_AXIS } from '../../../charts/common'; // @ts-ignore const VisComponent = lazy(() => import('./vis_component')); @@ -28,7 +30,9 @@ function shouldShowNoResultsMessage(visData: any, visType: XyVisType): boolean { return Boolean(isZeroHits); } -export const xyVisRenderer: ExpressionRenderDefinition = { +export const getXYVisRenderer: (deps: { + uiSettings: IUiSettingsClient; +}) => ExpressionRenderDefinition = ({ uiSettings }) => ({ name: visName, displayName: 'XY visualization', reuseDomNode: true, @@ -46,10 +50,11 @@ export const xyVisRenderer: ExpressionRenderDefinition = { fireEvent={handlers.event} uiState={handlers.uiState as PersistedState} syncColors={syncColors} + useLegacyTimeAxis={uiSettings.get(LEGACY_TIME_AXIS, false)} /> , domNode ); }, -}; +}); diff --git a/test/functional/apps/dashboard/dashboard_snapshots.ts b/test/functional/apps/dashboard/dashboard_snapshots.ts index 3aba671c0a4b29..9279bbd5806e7b 100644 --- a/test/functional/apps/dashboard/dashboard_snapshots.ts +++ b/test/functional/apps/dashboard/dashboard_snapshots.ts @@ -59,7 +59,7 @@ export default function ({ ); await PageObjects.dashboard.clickExitFullScreenLogoButton(); - expect(percentDifference).to.be.lessThan(0.02); + expect(percentDifference).to.be.lessThan(0.022); }); it('compare area chart snapshot', async () => { @@ -81,7 +81,7 @@ export default function ({ ); await PageObjects.dashboard.clickExitFullScreenLogoButton(); - expect(percentDifference).to.be.lessThan(0.02); + expect(percentDifference).to.be.lessThan(0.022); }); }); } diff --git a/test/functional/apps/visualize/_timelion.ts b/test/functional/apps/visualize/_timelion.ts index c531ada8a25737..712edb440311ff 100644 --- a/test/functional/apps/visualize/_timelion.ts +++ b/test/functional/apps/visualize/_timelion.ts @@ -167,7 +167,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(firstAreaChartData).to.eql(firstAreaExpectedChartData); expect(secondAreaChartData).to.eql(secondAreaExpectedChartData); expect(thirdAreaChartData).to.eql(thirdAreaExpectedChartData); - expect(firstAxesLabels).to.eql(['12.19GB', '12.2GB', '12.21GB']); + expect(firstAxesLabels).to.eql(['12.2GB', '12.21GB']); expect(secondAxesLabels).to.eql(['5.59KB', '5.6KB']); expect(thirdAxesLabels.toString()).to.be( 'BYTES_5721,BYTES_5722,BYTES_5723,BYTES_5724,BYTES_5725,BYTES_5726,BYTES_5727,BYTES_5728,BYTES_5729,BYTES_5730,BYTES_5731,BYTES_5732,BYTES_5733' diff --git a/test/functional/config.js b/test/functional/config.js index 5b0b79e84e8df3..09eccc863a0e53 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -54,6 +54,7 @@ export default async function ({ readConfigFile }) { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', 'visualization:visualize:legacyPieChartsLibrary': true, + 'visualization:useLegacyTimeAxis': true, }, }, diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap index b058c42d8b4d10..6601167e1f83a6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap @@ -67,6 +67,7 @@ exports[`xy_expression XYChart component it renders area 1`] = ` } } tickFormat={[Function]} + timeAxisLayerCount={0} title="c" /> { onClickValue, onSelectRange, syncColors: false, + useLegacyTimeAxis: false, }; }); diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index a3022e830c8615..80e4a8d4dc8f70 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -31,6 +31,8 @@ import { LabelOverflowConstraint, DisplayValueStyle, RecursivePartial, + AxisStyle, + GridLineStyle, ScaleType, } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; @@ -90,6 +92,7 @@ export type XYChartRenderProps = XYChartProps & { paletteService: PaletteRegistry; formatFactory: FormatFactory; timeZone: string; + useLegacyTimeAxis: boolean; minInterval: number | undefined; interactive?: boolean; onClickValue: (data: LensFilterEvent['data']) => void; @@ -130,6 +133,7 @@ export const getXyChartRenderer = (dependencies: { chartsActiveCursorService: ChartsPluginStart['activeCursor']; paletteService: PaletteRegistry; timeZone: string; + useLegacyTimeAxis: boolean; }): ExpressionRenderDefinition => ({ name: 'lens_xy_chart_renderer', displayName: 'XY chart', @@ -160,6 +164,7 @@ export const getXyChartRenderer = (dependencies: { chartsThemeService={dependencies.chartsThemeService} paletteService={dependencies.paletteService} timeZone={dependencies.timeZone} + useLegacyTimeAxis={dependencies.useLegacyTimeAxis} minInterval={calculateMinInterval(config)} interactive={handlers.isInteractive()} onClickValue={onClickValue} @@ -235,6 +240,7 @@ export function XYChart({ onSelectRange, interactive = true, syncColors, + useLegacyTimeAxis, }: XYChartRenderProps) { const { legend, @@ -320,15 +326,15 @@ export function XYChart({ filteredBarLayers.some((layer) => layer.accessors.length > 1) || filteredBarLayers.some((layer) => layer.splitAccessor); - const isTimeViz = data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time'); + const isTimeViz = Boolean(data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time')); const isHistogramViz = filteredLayers.every((l) => l.isHistogram); const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain( filteredLayers, data, minInterval, - Boolean(isTimeViz), - Boolean(isHistogramViz) + isTimeViz, + isHistogramViz ); const yAxesMap = { @@ -555,6 +561,72 @@ export function XYChart({ floatingColumns: legend?.floatingColumns ?? 1, } as LegendPositionConfig; + // todo be moved in the chart plugin + const shouldUseNewTimeAxis = isTimeViz && !useLegacyTimeAxis && !shouldRotate; + + const gridLineStyle: RecursivePartial = shouldUseNewTimeAxis + ? { + visible: gridlinesVisibilitySettings?.x, + strokeWidth: 0.1, + stroke: darkMode ? 'white' : 'black', + } + : { + visible: gridlinesVisibilitySettings?.x, + strokeWidth: 2, + }; + const xAxisStyle: RecursivePartial = shouldUseNewTimeAxis + ? { + tickLabel: { + visible: Boolean(tickLabelsVisibilitySettings?.x), + rotation: 0, // rotation is disabled on new time axis + fontSize: 11, + padding: + referenceLinePaddings.bottom != null ? { inner: referenceLinePaddings.bottom } : 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: Boolean(tickLabelsVisibilitySettings?.x), + }, + axisTitle: { + visible: axisTitlesVisibilitySettings.x, + padding: + !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + } + : { + tickLabel: { + visible: tickLabelsVisibilitySettings?.x, + rotation: labelsOrientation?.x, + padding: + referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + axisTitle: { + visible: axisTitlesVisibilitySettings.x, + padding: + !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + }; return ( safeXAccessorLabelRenderer(d.value), }} - allowBrushingLastHistogramBin={Boolean(isTimeViz)} + allowBrushingLastHistogramBin={isTimeViz} rotation={shouldRotate ? 90 : 0} xDomain={xDomain} onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined} @@ -618,29 +690,11 @@ export function XYChart({ id="x" position={shouldRotate ? Position.Left : Position.Bottom} title={xTitle} - gridLine={{ - visible: gridlinesVisibilitySettings?.x, - strokeWidth: 2, - }} + gridLine={gridLineStyle} hide={filteredLayers[0].hide || !filteredLayers[0].xAccessor} tickFormat={(d) => safeXAccessorLabelRenderer(d)} - style={{ - tickLabel: { - visible: tickLabelsVisibilitySettings?.x, - rotation: labelsOrientation?.x, - padding: - referenceLinePaddings.bottom != null - ? { inner: referenceLinePaddings.bottom } - : undefined, - }, - axisTitle: { - visible: axisTitlesVisibilitySettings.x, - padding: - !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null - ? { inner: referenceLinePaddings.bottom } - : undefined, - }, - }} + style={xAxisStyle} + timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} /> {yAxesConfiguration.map((axis) => { diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index f9d48ffaaae37b..d3d461e8f8741e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -12,6 +12,7 @@ import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public import type { LensPluginStartDependencies } from '../plugin'; import { getTimeZone } from '../utils'; import type { FormatFactory } from '../../common'; +import { LEGACY_TIME_AXIS } from '../../../../../src/plugins/charts/common'; export interface XyVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; @@ -37,6 +38,7 @@ export class XyVisualization { chartsActiveCursorService: charts.activeCursor, paletteService: palettes, timeZone: getTimeZone(core.uiSettings), + useLegacyTimeAxis: core.uiSettings.get(LEGACY_TIME_AXIS), }) ); return getXyVisualization({ paletteService: palettes, fieldFormats }); diff --git a/yarn.lock b/yarn.lock index 3e4e3ad4ed2243..56d5cf17791eaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2337,10 +2337,10 @@ dependencies: object-hash "^1.3.0" -"@elastic/charts@38.0.1": - version "38.0.1" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-38.0.1.tgz#9c1db7e0f1de869e0b2b505e192bbb9d62d60dc8" - integrity sha512-i9mIA3Ji9jSjuFDtuh9gV1xpCl3sbBEDgJiOgLVt04pr/qZH2W+tr3AV5yHvjsR7Te0Pmh/Cm5wLBvFKaI1nIA== +"@elastic/charts@38.1.0": + version "38.1.0" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-38.1.0.tgz#52146564c0e399da2267c10ec4536c33e50969e4" + integrity sha512-u2hQ8+daCvqapKQiFqN8QHWTz3OXby5Xf/ta1Dv59KTDTFIinCZD/M8PyD3MapbKx4b67hL7UxbErqr4rZ8jeA== dependencies: "@popperjs/core" "^2.4.0" chroma-js "^2.1.0" @@ -2351,6 +2351,7 @@ d3-interpolate "^1.4.0" d3-scale "^1.0.7" d3-shape "^1.3.4" + luxon "^1.25.0" prop-types "^15.7.2" re-reselect "^3.4.0" react-redux "^7.1.0" @@ -19720,6 +19721,11 @@ lru-queue@0.1: dependencies: es5-ext "~0.10.2" +luxon@^1.25.0: + version "1.28.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" + integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + lz-string@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" From 017fcac4b9e029b1144171b1dcfcec4ad9d10a3b Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Wed, 27 Oct 2021 10:53:07 -0700 Subject: [PATCH 14/46] Removes functional tests using freeze API (#116373) Signed-off-by: Tyler Smalley --- .../reporting_and_security/index.ts | 1 - .../search_frozen_indices.ts | 127 ------------------ 2 files changed, 128 deletions(-) delete mode 100644 x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts index 159115e2054e1e..f6654ff5a6b1d4 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts @@ -30,6 +30,5 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./spaces')); loadTestFile(require.resolve('./usage')); loadTestFile(require.resolve('./ilm_migration_apis')); - loadTestFile(require.resolve('./search_frozen_indices')); }); } diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts b/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts deleted file mode 100644 index daa749649e250e..00000000000000 --- a/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function ({ getService }: FtrProviderContext) { - const kibanaServer = getService('kibanaServer'); - const supertestSvc = getService('supertest'); - const esSupertest = getService('esSupertest'); - const indexPatternId = 'cool-test-index-pattern'; - - async function callExportAPI() { - const job = { - browserTimezone: 'UTC', - columns: ['@timestamp', 'ip', 'utilization'], - searchSource: { - fields: [{ field: '*', include_unmapped: 'true' }], - filter: [ - { - meta: { field: '@timestamp', index: indexPatternId, params: {} }, - range: { - '@timestamp': { - format: 'strict_date_optional_time', - gte: '2020-08-24T00:00:00.000Z', - lte: '2022-08-24T21:40:48.346Z', - }, - }, - }, - ], - index: indexPatternId, - parent: { filter: [], index: indexPatternId, query: { language: 'kuery', query: '' } }, - sort: [{ '@timestamp': 'desc' }], - trackTotalHits: true, - }, - title: 'Test search', - }; - - return await supertestSvc - .post(`/api/reporting/v1/generate/immediate/csv_searchsource`) - .set('kbn-xsrf', 'xxx') - .send(job); - } - - describe('Frozen indices search', () => { - const reset = async () => { - await kibanaServer.uiSettings.replace({ 'search:includeFrozen': false }); - try { - await esSupertest.delete('/test1,test2,test3'); - await kibanaServer.savedObjects.delete({ type: 'index-pattern', id: indexPatternId }); - } catch (err) { - // ignore 404 error - } - }; - - before(reset); - after(reset); - - it('Search includes frozen indices based on Advanced Setting', async () => { - await kibanaServer.uiSettings.update({ 'csv:quoteValues': true }); - - // setup: add multiple indices of test data - await Promise.all([ - esSupertest - .post('/test1/_doc') - .send({ '@timestamp': '2021-08-24T21:36:40Z', ip: '43.98.8.183', utilization: 18725 }), - esSupertest - .post('/test2/_doc') - .send({ '@timestamp': '2021-08-21T09:36:40Z', ip: '63.91.103.79', utilization: 8480 }), - esSupertest - .post('/test3/_doc') - .send({ '@timestamp': '2021-08-17T21:36:40Z', ip: '139.108.162.171', utilization: 3078 }), - ]); - await esSupertest.post('/test*/_refresh'); - - // setup: create index pattern - const indexPatternCreateResponse = await kibanaServer.savedObjects.create({ - type: 'index-pattern', - id: indexPatternId, - overwrite: true, - attributes: { title: 'test*', timeFieldName: '@timestamp' }, - }); - expect(indexPatternCreateResponse.id).to.be(indexPatternId); - - // 1. check the initial data with a CSV export - const initialSearch = await callExportAPI(); - expectSnapshot(initialSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - \\"Aug 17, 2021 @ 21:36:40.000\\",\\"139.108.162.171\\",\\"3,078\\" - " - `); - - // 2. freeze an index in the pattern - await esSupertest.post('/test3/_freeze').expect(200); - await esSupertest.post('/test*/_refresh').expect(200); - - // 3. recheck the search results - const afterFreezeSearch = await callExportAPI(); - expectSnapshot(afterFreezeSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - " - `); - - // 4. update setting to allow searching frozen data - await kibanaServer.uiSettings.update({ 'search:includeFrozen': true }); - - // 5. recheck the search results - const afterAllowSearch = await callExportAPI(); - expectSnapshot(afterAllowSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - \\"Aug 17, 2021 @ 21:36:40.000\\",\\"139.108.162.171\\",\\"3,078\\" - " - `); - }); - }); -} From bd2a7cefa55d02ef14356ebb590ba89ffa2f7679 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Wed, 27 Oct 2021 13:42:11 -0500 Subject: [PATCH 15/46] [DOCS] Fixes monitoring setting (#116309) --- docs/settings/spaces-settings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings/spaces-settings.asciidoc b/docs/settings/spaces-settings.asciidoc index 5483912387ceca..dd379431011454 100644 --- a/docs/settings/spaces-settings.asciidoc +++ b/docs/settings/spaces-settings.asciidoc @@ -13,7 +13,7 @@ return all spaces using a single `_search` from {es}, so you must configure this setting lower than the `index.max_result_window` in {es}. The default is `1000`. -`monitoring.cluster_alerts-allowedSpaces` {ess-icon}:: +`monitoring.cluster_alerts.allowedSpaces` {ess-icon}:: Specifies the spaces where cluster alerts are automatically generated. You must specify all spaces where you want to generate alerts, including the default space. When the default space is unspecified, {kib} is unable to generate an alert for the default space. From 136d322bddce84c34c1192aa917cfce8193a11d5 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Wed, 27 Oct 2021 21:55:55 +0200 Subject: [PATCH 16/46] adds lists plugin to security solution cypress tests execution (#116442) --- .buildkite/scripts/pipelines/pull_request/pipeline.js | 3 +++ vars/tasks.groovy | 1 + 2 files changed, 4 insertions(+) diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.js b/.buildkite/scripts/pipelines/pull_request/pipeline.js index 02d6fc270ddb01..c5ed216042b680 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.js +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.js @@ -57,6 +57,9 @@ const uploadPipeline = (pipelineContent) => { if ( (await doAnyChangesMatch([ /^x-pack\/plugins\/security_solution/, + /^x-pack\/plugins\/cases/, + /^x-pack\/plugins\/lists/, + /^x-pack\/plugins\/timelines/, /^x-pack\/test\/security_solution_cypress/, /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/sections\/action_connector_form/, /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/context\/actions_connectors_context\.tsx/, diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 0f509fa8ba1327..050b62646fb3bc 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -137,6 +137,7 @@ def functionalXpack(Map params = [:]) { 'x-pack/plugins/security_solution/', 'x-pack/plugins/cases/', 'x-pack/plugins/timelines/', + 'x-pack/plugins/lists/', 'x-pack/test/security_solution_cypress/', 'x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/', 'x-pack/plugins/triggers_actions_ui/public/application/context/actions_connectors_context.tsx', From c3f207a071c8bc11a4ab4abcf4bf0ed1f8a91c53 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Wed, 27 Oct 2021 16:18:19 -0400 Subject: [PATCH 17/46] Revert "[App Search] Load curation settings at root /curations/ path (#115690)" (#116462) --- .../curations/curations_router.test.tsx | 100 ------------------ .../components/curations/curations_router.tsx | 35 +----- .../components/curations/views/curations.tsx | 12 +-- .../curations_settings.test.tsx | 61 +++++++++++ .../curations_settings/curations_settings.tsx | 26 ++++- 5 files changed, 90 insertions(+), 144 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx index c0278c765e85ee..9598212d3e0c98 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx @@ -5,9 +5,6 @@ * 2.0. */ -import '../../../__mocks__/shallow_useeffect.mock'; -import '../../../__mocks__/react_router'; -import { setMockActions, setMockValues } from '../../../__mocks__/kea_logic'; import '../../__mocks__/engine_logic.mock'; import React from 'react'; @@ -15,110 +12,13 @@ import { Route, Switch } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { LogRetentionOptions } from '../log_retention'; - import { CurationsRouter } from './'; -const MOCK_VALUES = { - // CurationsSettingsLogic - dataLoading: false, - curationsSettings: { - enabled: true, - mode: 'automatic', - }, - // LogRetentionLogic - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: true, - }, - }, - // LicensingLogic - hasPlatinumLicense: true, -}; - -const MOCK_ACTIONS = { - // CurationsSettingsLogic - loadCurationsSettings: jest.fn(), - onSkipLoadingCurationsSettings: jest.fn(), - // LogRetentionLogic - fetchLogRetention: jest.fn(), -}; - describe('CurationsRouter', () => { - beforeEach(() => { - jest.clearAllMocks(); - setMockActions(MOCK_ACTIONS); - }); - it('renders', () => { const wrapper = shallow(); expect(wrapper.find(Switch)).toHaveLength(1); expect(wrapper.find(Route)).toHaveLength(4); }); - - it('loads log retention settings', () => { - setMockValues(MOCK_VALUES); - shallow(); - - expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalled(); - }); - - describe('when the user has no platinum license', () => { - beforeEach(() => { - setMockValues({ - ...MOCK_VALUES, - hasPlatinumLicense: false, - }); - }); - - it('it does not fetch log retention', () => { - shallow(); - expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalledTimes(0); - }); - }); - - describe('loading curation settings based on log retention', () => { - it('loads curation settings when log retention is enabled', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: true, - }, - }, - }); - - shallow(); - - expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1); - }); - - it('skips loading curation settings when log retention is disabled', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: false, - }, - }, - }); - - shallow(); - - expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(1); - }); - - it('takes no action if log retention has not yet been loaded', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: null, - }); - - shallow(); - - expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(0); - expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(0); - }); - }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx index a3b000ea5054ac..693e5406b714b3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx @@ -5,53 +5,20 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { Route, Switch } from 'react-router-dom'; -import { useValues, useActions } from 'kea'; - -import { LicensingLogic } from '../../../shared/licensing'; import { ENGINE_CURATIONS_PATH, ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH, ENGINE_CURATION_SUGGESTION_PATH, } from '../../routes'; -import { LogRetentionLogic, LogRetentionOptions } from '../log_retention'; import { Curation } from './curation'; import { Curations, CurationCreation, CurationSuggestion } from './views'; -import { CurationsSettingsLogic } from './views/curations_settings'; export const CurationsRouter: React.FC = () => { - // We need to loadCurationsSettings here so they are available across all views - - const { hasPlatinumLicense } = useValues(LicensingLogic); - - const { loadCurationsSettings, onSkipLoadingCurationsSettings } = - useActions(CurationsSettingsLogic); - - const { logRetention } = useValues(LogRetentionLogic); - const { fetchLogRetention } = useActions(LogRetentionLogic); - - const analyticsDisabled = !logRetention?.[LogRetentionOptions.Analytics].enabled; - - useEffect(() => { - if (hasPlatinumLicense) { - fetchLogRetention(); - } - }, [hasPlatinumLicense]); - - useEffect(() => { - if (logRetention) { - if (!analyticsDisabled) { - loadCurationsSettings(); - } else { - onSkipLoadingCurationsSettings(); - } - } - }, [logRetention]); - return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx index 2207555772b5bf..b0f4f03789af22 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx @@ -24,17 +24,15 @@ import { getCurationsBreadcrumbs } from '../utils'; import { CurationsHistory } from './curations_history/curations_history'; import { CurationsOverview } from './curations_overview'; -import { CurationsSettings, CurationsSettingsLogic } from './curations_settings'; +import { CurationsSettings } from './curations_settings'; export const Curations: React.FC = () => { - const { dataLoading: curationsDataLoading, meta, selectedPageTab } = useValues(CurationsLogic); + const { dataLoading, meta, selectedPageTab } = useValues(CurationsLogic); const { loadCurations, onSelectPageTab } = useActions(CurationsLogic); const { engine: { search_relevance_suggestions_active: searchRelevanceSuggestionsActive }, } = useValues(EngineLogic); - const { dataLoading: curationsSettingsDataLoading } = useValues(CurationsSettingsLogic); - const suggestionsEnabled = searchRelevanceSuggestionsActive; const OVERVIEW_TAB = { @@ -82,8 +80,6 @@ export const Curations: React.FC = () => { loadCurations(); }, [meta.page.current]); - const isLoading = curationsSettingsDataLoading || curationsDataLoading; - return ( { {CREATE_NEW_CURATION_TITLE} , ], - tabs: isLoading ? undefined : pageTabs, + tabs: dataLoading ? undefined : pageTabs, }} - isLoading={isLoading} + isLoading={dataLoading} > {selectedPageTab === 'overview' && } {selectedPageTab === 'history' && } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx index 3b01d1e41c2718..4b4e11c31d4b88 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx @@ -17,6 +17,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EuiButtonEmpty, EuiCallOut, EuiSwitch } from '@elastic/eui'; +import { mountWithIntl } from '@kbn/test/jest'; + import { Loading } from '../../../../../shared/loading'; import { EuiButtonTo } from '../../../../../shared/react_router_helpers'; import { DataPanel } from '../../../data_panel'; @@ -44,6 +46,8 @@ const MOCK_VALUES = { const MOCK_ACTIONS = { // CurationsSettingsLogic + loadCurationsSettings: jest.fn(), + onSkipLoadingCurationsSettings: jest.fn(), toggleCurationsEnabled: jest.fn(), toggleCurationsMode: jest.fn(), // LogRetentionLogic @@ -56,6 +60,14 @@ describe('CurationsSettings', () => { setMockActions(MOCK_ACTIONS); }); + it('loads curations and log retention settings on load', () => { + setMockValues(MOCK_VALUES); + mountWithIntl(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalled(); + expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalled(); + }); + it('contains a switch to toggle curations settings', () => { let wrapper: ShallowWrapper; @@ -154,6 +166,50 @@ describe('CurationsSettings', () => { expect(wrapper.is(Loading)).toBe(true); }); + describe('loading curation settings based on log retention', () => { + it('loads curation settings when log retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: true, + }, + }, + }); + + shallow(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1); + }); + + it('skips loading curation settings when log retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: false, + }, + }, + }); + + shallow(); + + expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(1); + }); + + it('takes no action if log retention has not yet been loaded', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: null, + }); + + shallow(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(0); + expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(0); + }); + }); + describe('when the user has no platinum license', () => { beforeEach(() => { setMockValues({ @@ -162,6 +218,11 @@ describe('CurationsSettings', () => { }); }); + it('it does not fetch log retention', () => { + shallow(); + expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalledTimes(0); + }); + it('shows a CTA to upgrade your license when the user when the user', () => { const wrapper = shallow(); expect(wrapper.is(DataPanel)).toBe(true); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx index a5d4a33d8b870b..de669298b11d95 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { useActions, useValues } from 'kea'; @@ -43,12 +43,34 @@ export const CurationsSettings: React.FC = () => { curationsSettings: { enabled, mode }, dataLoading, } = useValues(CurationsSettingsLogic); - const { toggleCurationsEnabled, toggleCurationsMode } = useActions(CurationsSettingsLogic); + const { + loadCurationsSettings, + onSkipLoadingCurationsSettings, + toggleCurationsEnabled, + toggleCurationsMode, + } = useActions(CurationsSettingsLogic); const { isLogRetentionUpdating, logRetention } = useValues(LogRetentionLogic); + const { fetchLogRetention } = useActions(LogRetentionLogic); const analyticsDisabled = !logRetention?.[LogRetentionOptions.Analytics].enabled; + useEffect(() => { + if (hasPlatinumLicense) { + fetchLogRetention(); + } + }, [hasPlatinumLicense]); + + useEffect(() => { + if (logRetention) { + if (!analyticsDisabled) { + loadCurationsSettings(); + } else { + onSkipLoadingCurationsSettings(); + } + } + }, [logRetention]); + if (!hasPlatinumLicense) return ( Date: Wed, 27 Oct 2021 14:22:45 -0600 Subject: [PATCH 18/46] [Security Solutions] Adds bsearch service to FTR e2e tests to reduce flake, boilerplate, and technique choices (#116211) ## Summary Fixes flake tests of: https://github.com/elastic/kibana/issues/115918 https://github.com/elastic/kibana/issues/103273 https://github.com/elastic/kibana/issues/108640 https://github.com/elastic/kibana/issues/109447 https://github.com/elastic/kibana/issues/100630 https://github.com/elastic/kibana/issues/94535 https://github.com/elastic/kibana/issues/104260 Security solution has been using `bsearch` and has encountered flake in various forms. Different developers have been fixing the flake in a few odd ways (myself included) which aren't 100%. This PR introduces a once-in-for-all REST API retry service called `bsearch` which will query `bsearch` and if `bsearch` is not completed because of async occurring due to slower CI runtimes it will continuously call into the `bsearch` with the correct API to ensure it gets a complete response before returning. ## Usage Anyone can use this service like so: ```ts const bsearch = getService('bsearch'); const response = await bsearch.send({ supertest, options: { defaultIndex: ['large_volume_dns_data'], } strategy: 'securitySolutionSearchStrategy', }); ``` If you're using a custom auth then you can set that beforehand like so: ```ts const bsearch = getService('bsearch'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const supertest supertestWithoutAuth.auth(username, password); const response = await bsearch.send({ supertest, options: { defaultIndex: ['large_volume_dns_data'], } strategy: 'securitySolutionSearchStrategy', }); ``` ## Misconceptions in the tests leading to flake * Can you just call the bsearch REST API and it will always return data first time? Not always true, as when CI slows down or data increases `bsearch` will give you back an async reference and then your test will blow up. * Can we wrap the REST API in `retry` to fix the flake? Not always but mostly true, as when CI slows down or data increases `bsearch` could return the async version continuously which could then fail your test. It's also tedious to tell everyone in code reviews to wrap everything in `retry` instead of just fixing it with a service as well as inform new people why we are constantly wrapping these tests in `retry`. * Can we manually parse the `bsearch` if it has `async` for each test? This is true but is error prone and I did this for one test and it's ugly and I had issues as I have to wrap 2 things in `retry` and test several conditions. Also it's harder for people to read the tests rather than just reading there is a service call. Also people in code reviews missed where I had bugs with it. Also lots of boiler plate. * Can we just increase the timeout with `wait_for_completion_timeout` and the tests will pass for sure then? Not true today but maybe true later, as this hasn't been added as plumbing yet. See this [open ticket](https://github.com/elastic/kibana/issues/107241). Even if it is and we increase the timeout to a very large number bsearch might return with an `async` or you might want to test the `async` path. Either way, if/when we add the ability we can increase it within 1 spot which is this service for everyone rather than going to each individual test to add it. If/when it's added if people don't use the bsearch service we can remove it later if we find this is deterministic enough and no one wants to test bsearch features with their strategies down the road. ## Manual test of bsearch service If you want to manually watch the bsearch operate as if the CI system is running slow or to cause an `async` manually you manually modify this setting here: https://github.com/elastic/kibana/blob/master/src/plugins/data/server/search/strategies/ese_search/request_utils.ts#L61 To be of a lower number such as `1ms` and then you will see it enter the `async` code within `bsearch` consistently ## Reference PRs We cannot set the wait_for_complete just yet https://github.com/elastic/kibana/issues/107241 so we decided this was the best way to reduce flake for testing for now. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- test/common/services/bsearch.ts | 122 ++++++++ test/common/services/index.ts | 2 + .../apis/security_solution/authentications.ts | 43 +-- .../apis/security_solution/events.ts | 33 ++- .../apis/security_solution/host_details.ts | 29 +- .../apis/security_solution/hosts.ts | 135 +++++---- .../apis/security_solution/kpi_hosts.ts | 265 +++++++++--------- .../apis/security_solution/kpi_network.ts | 176 ++++++------ .../security_solution/matrix_dns_histogram.ts | 95 ++----- .../apis/security_solution/network_details.ts | 61 ++-- .../apis/security_solution/network_dns.ts | 36 +-- .../security_solution/network_top_n_flow.ts | 63 +++-- .../apis/security_solution/overview_host.ts | 29 +- .../security_solution/overview_network.ts | 77 ++--- .../security_solution/timeline_details.ts | 67 +++-- .../apis/security_solution/tls.ts | 72 ++--- .../security_solution/uncommon_processes.ts | 142 +++++----- .../apis/security_solution/users.ts | 80 +++--- .../security_and_spaces/tests/basic/events.ts | 6 + .../security_and_spaces/tests/trial/events.ts | 4 + 20 files changed, 819 insertions(+), 718 deletions(-) create mode 100644 test/common/services/bsearch.ts diff --git a/test/common/services/bsearch.ts b/test/common/services/bsearch.ts new file mode 100644 index 00000000000000..d9fe89d9e4b9c7 --- /dev/null +++ b/test/common/services/bsearch.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import request from 'superagent'; +import type SuperTest from 'supertest'; +import { IEsSearchResponse } from 'src/plugins/data/common'; +import { FtrProviderContext } from '../ftr_provider_context'; +import { RetryService } from './retry/retry'; + +/** + * Function copied from here: + * test/api_integration/apis/search/bsearch.ts without the compress + * + * Splits the JSON lines from bsearch + */ +const parseBfetchResponse = (resp: request.Response): Array> => { + return resp.text + .trim() + .split('\n') + .map((item) => JSON.parse(item)); +}; + +/** + * Function copied from here: + * x-pack/test/rule_registry/common/lib/authentication/spaces.ts + * @param spaceId The space id we want to utilize + */ +const getSpaceUrlPrefix = (spaceId?: string): string => { + return spaceId && spaceId !== 'default' ? `/s/${spaceId}` : ``; +}; + +/** + * Options for the send method + */ +interface SendOptions { + supertest: SuperTest.SuperTest; + options: object; + strategy: string; + space?: string; +} + +/** + * Bsearch factory which will return a new bsearch capable service that can reduce flake + * on the CI systems when they are under pressure and bsearch returns an async search + * response or a sync response. + * + * @example + * const supertest = getService('supertest'); + * const bsearch = getService('bsearch'); + * const response = await bsearch.send({ + * supertest, + * options: { + * defaultIndex: ['large_volume_dns_data'], + * } + * strategy: 'securitySolutionSearchStrategy', + * }); + * expect(response).eql({ ... your value ... }); + */ +export const BSearchFactory = (retry: RetryService) => ({ + /** Send method to send in your supertest, url, options, and strategy name */ + send: async ({ + supertest, + options, + strategy, + space, + }: SendOptions): Promise => { + const spaceUrl = getSpaceUrlPrefix(space); + const { body } = await retry.try(async () => { + return supertest + .post(`${spaceUrl}/internal/search/${strategy}`) + .set('kbn-xsrf', 'true') + .send(options) + .expect(200); + }); + + if (body.isRunning) { + const result = await retry.try(async () => { + const resp = await supertest + .post(`${spaceUrl}/internal/bsearch`) + .set('kbn-xsrf', 'true') + .send({ + batch: [ + { + request: { + id: body.id, + ...options, + }, + options: { + strategy, + }, + }, + ], + }) + .expect(200); + const [parsedResponse] = parseBfetchResponse(resp); + expect(parsedResponse.result.isRunning).equal(false); + return parsedResponse.result; + }); + return result; + } else { + return body; + } + }, +}); + +/** + * Bsearch provider which will return a new bsearch capable service that can reduce flake + * on the CI systems when they are under pressure and bsearch returns an async search response + * or a sync response. + */ +export function BSearchProvider({ + getService, +}: FtrProviderContext): ReturnType { + const retry = getService('retry'); + return BSearchFactory(retry); +} diff --git a/test/common/services/index.ts b/test/common/services/index.ts index c04bd778468a9e..91d17ce1bb3e89 100644 --- a/test/common/services/index.ts +++ b/test/common/services/index.ts @@ -16,6 +16,7 @@ import { SecurityServiceProvider } from './security'; import { EsDeleteAllIndicesProvider } from './es_delete_all_indices'; import { SavedObjectInfoService } from './saved_object_info'; import { IndexPatternsService } from './index_patterns'; +import { BSearchProvider } from './bsearch'; export const services = { deployment: DeploymentService, @@ -28,4 +29,5 @@ export const services = { esDeleteAllIndices: EsDeleteAllIndicesProvider, savedObjectInfo: SavedObjectInfoService, indexPatterns: IndexPatternsService, + bsearch: BSearchProvider, }; diff --git a/x-pack/test/api_integration/apis/security_solution/authentications.ts b/x-pack/test/api_integration/apis/security_solution/authentications.ts index 4ea8b8ab82e16f..8254ce034d2a5b 100644 --- a/x-pack/test/api_integration/apis/security_solution/authentications.ts +++ b/x-pack/test/api_integration/apis/security_solution/authentications.ts @@ -6,7 +6,10 @@ */ import expect from '@kbn/expect'; -import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostAuthenticationsStrategyResponse, + HostsQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -22,16 +25,19 @@ const EDGE_LENGTH = 1; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('authentications', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts')); + before(async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); + + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts') + ); it('Make sure that we get Authentication data', async () => { - const { body: authentications } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const authentications = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.authentications, timerange: { interval: '12h', @@ -47,9 +53,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['auditbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(authentications.edges.length).to.be(EDGE_LENGTH); expect(authentications.totalCount).to.be(TOTAL_COUNT); @@ -57,10 +63,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that pagination is working in Authentications query', async () => { - const { body: authentications } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const authentications = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.authentications, timerange: { interval: '12h', @@ -76,16 +81,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['auditbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(authentications.edges.length).to.be(EDGE_LENGTH); expect(authentications.totalCount).to.be(TOTAL_COUNT); - expect(authentications.edges[0]!.node.lastSuccess!.source!.ip).to.eql([ + expect(authentications.edges[0].node.lastSuccess?.source?.ip).to.eql([ LAST_SUCCESS_SOURCE_IP, ]); - expect(authentications.edges[0]!.node.lastSuccess!.host!.name).to.eql([HOST_NAME]); + expect(authentications.edges[0].node.lastSuccess?.host?.name).to.eql([HOST_NAME]); }); }); } diff --git a/x-pack/test/api_integration/apis/security_solution/events.ts b/x-pack/test/api_integration/apis/security_solution/events.ts index f6a668679b11d2..fef37e9939fcb3 100644 --- a/x-pack/test/api_integration/apis/security_solution/events.ts +++ b/x-pack/test/api_integration/apis/security_solution/events.ts @@ -11,13 +11,13 @@ import { JsonObject } from '@kbn/utility-types'; import { Direction, TimelineEventsQueries, + TimelineEventsAllStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; import { getDocValueFields, getFieldsToRequest, getFilterValue } from './utils'; const TO = '3000-01-01T00:00:00.000Z'; const FROM = '2000-01-01T00:00:00.000Z'; -const TEST_URL = '/internal/search/timelineSearchStrategy/'; // typical values that have to change after an update from "scripts/es_archiver" const DATA_COUNT = 7; const HOST_NAME = 'suricata-sensor-amsterdam'; @@ -30,6 +30,8 @@ const LIMITED_PAGE_SIZE = 2; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); + const getPostBody = (): JsonObject => ({ defaultIndex: ['auditbeat-*'], docValueFields: getDocValueFields(), @@ -66,14 +68,14 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns Timeline data', async () => { - const resp = await supertest - .post(TEST_URL) - .set('kbn-xsrf', 'true') - .set('Content-Type', 'application/json') - .send(getPostBody()) - .expect(200); + const timeline = await bsearch.send({ + supertest, + options: { + ...getPostBody(), + }, + strategy: 'timelineSearchStrategy', + }); - const timeline = resp.body; expect(timeline.edges.length).to.be(EDGE_LENGTH); expect(timeline.edges[0].node.data.length).to.be(DATA_COUNT); expect(timeline.totalCount).to.be(TOTAL_COUNT); @@ -82,20 +84,17 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns paginated Timeline query', async () => { - const resp = await supertest - .post(TEST_URL) - .set('kbn-xsrf', 'true') - .set('Content-Type', 'application/json') - .send({ + const timeline = await bsearch.send({ + supertest, + options: { ...getPostBody(), pagination: { activePage: 0, querySize: LIMITED_PAGE_SIZE, }, - }) - .expect(200); - - const timeline = resp.body; + }, + strategy: 'timelineSearchStrategy', + }); expect(timeline.edges.length).to.be(LIMITED_PAGE_SIZE); expect(timeline.edges[0].node.data.length).to.be(DATA_COUNT); expect(timeline.totalCount).to.be(TOTAL_COUNT); diff --git a/x-pack/test/api_integration/apis/security_solution/host_details.ts b/x-pack/test/api_integration/apis/security_solution/host_details.ts index 114f60a21c4e35..d2de0f84a37698 100644 --- a/x-pack/test/api_integration/apis/security_solution/host_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/host_details.ts @@ -7,16 +7,24 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostDetailsStrategyResponse, + HostsQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Host Details', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -213,12 +221,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get HostDetails data', async () => { - const { - body: { hostDetails }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { hostDetails } = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.details, timerange: { interval: '12h', @@ -229,9 +234,9 @@ export default function ({ getService }: FtrProviderContext) { docValueFields: [], hostName: 'raspberrypi', inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(hostDetails).to.eql(expectedResult.hostDetails); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/hosts.ts b/x-pack/test/api_integration/apis/security_solution/hosts.ts index 4df46002e9a13f..bb2969f85a98b8 100644 --- a/x-pack/test/api_integration/apis/security_solution/hosts.ts +++ b/x-pack/test/api_integration/apis/security_solution/hosts.ts @@ -10,6 +10,9 @@ import { HostsQueries, Direction, HostsFields, + HostsStrategyResponse, + HostDetailsStrategyResponse, + HostFirstLastSeenStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -26,17 +29,19 @@ const CURSOR_ID = '2ab45fc1c41e4c84bbd02202a7e5761f'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); - // Failing: See https://github.com/elastic/kibana/issues/104260 - describe.skip('hosts', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts')); + describe('hosts', () => { + before(async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); + + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts') + ); it('Make sure that we get Hosts Table data', async () => { - const { body: hosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const hosts = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.hosts, timerange: { interval: '12h', @@ -56,19 +61,18 @@ export default function ({ getService }: FtrProviderContext) { querySize: 1, }, inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(hosts.edges.length).to.be(EDGE_LENGTH); expect(hosts.totalCount).to.be(TOTAL_COUNT); expect(hosts.pageInfo.fakeTotalCount).to.equal(3); }); it('Make sure that pagination is working in Hosts Table query', async () => { - const { body: hosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const hosts = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.hosts, timerange: { interval: '12h', @@ -88,16 +92,32 @@ export default function ({ getService }: FtrProviderContext) { querySize: 2, }, inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(hosts.edges.length).to.be(EDGE_LENGTH); expect(hosts.totalCount).to.be(TOTAL_COUNT); - expect(hosts.edges[0]!.node.host!.os!.name).to.eql([HOST_NAME]); + expect(hosts.edges[0].node.host?.os?.name).to.eql([HOST_NAME]); }); it('Make sure that we get Host details data', async () => { - const expectedHostDetails = { + const { hostDetails } = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsQueries.details, + hostName: 'zeek-sensor-san-francisco', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(hostDetails).to.eql({ _id: 'zeek-sensor-san-francisco', host: { architecture: ['x86_64'], @@ -122,91 +142,66 @@ export default function ({ getService }: FtrProviderContext) { provider: ['digitalocean'], region: ['sfo2'], }, - }; - const { - body: { hostDetails }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsQueries.details, - hostName: 'zeek-sensor-san-francisco', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - - expect(hostDetails).to.eql(expectedHostDetails); + }); }); it('Make sure that we get First Seen for a Host without docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*'], docValueFields: [], hostName: 'zeek-sensor-san-francisco', order: 'asc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.firstSeen).to.eql('2019-02-19T19:36:23.561Z'); }); it('Make sure that we get Last Seen for a Host without docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*'], docValueFields: [], hostName: 'zeek-sensor-san-francisco', order: 'desc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.lastSeen).to.eql('2019-02-19T20:42:33.561Z'); }); it('Make sure that we get First Seen for a Host with docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], docValueFields: [{ field: '@timestamp', format: 'epoch_millis' }], hostName: 'zeek-sensor-san-francisco', order: 'asc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.firstSeen).to.eql(new Date('2019-02-19T19:36:23.561Z').valueOf()); }); it('Make sure that we get Last Seen for a Host with docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], docValueFields: [{ field: '@timestamp', format: 'epoch_millis' }], hostName: 'zeek-sensor-san-francisco', order: 'desc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.lastSeen).to.eql(new Date('2019-02-19T20:42:33.561Z').valueOf()); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts index 632f738d85f362..f38cf406a9dbe5 100644 --- a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts +++ b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts @@ -6,18 +6,27 @@ */ import expect from '@kbn/expect'; -import { HostsKpiQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostsKpiAuthenticationsStrategyResponse, + HostsKpiHostsStrategyResponse, + HostsKpiQueries, + HostsKpiUniqueIpsStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Kpi Hosts', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/kpi_hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/kpi_hosts')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/kpi_hosts') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/kpi_hosts') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -50,88 +59,80 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiHosts data', async () => { - await retry.try(async () => { - const { body: kpiHosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiHosts, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['filebeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - - expect(kpiHosts.hostsHistogram!).to.eql(expectedResult.hostsHistogram); - expect(kpiHosts.hosts!).to.eql(expectedResult.hosts); + const kpiHosts = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiHosts, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(kpiHosts.hostsHistogram).to.eql(expectedResult.hostsHistogram); + expect(kpiHosts.hosts).to.eql(expectedResult.hosts); }); it('Make sure that we get KpiAuthentications data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiAuthentications, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['filebeat-*'], - docValueFields: [], - inspect: false, - /* We need a very long timeout to avoid returning just partial data. - ** https://github.com/elastic/kibana/blob/master/x-pack/test/api_integration/apis/search/search.ts#L18 - */ - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.authenticationsSuccess!).to.eql(expectedResult.authSuccess); - expect(body.authenticationsSuccessHistogram!).to.eql(expectedResult.authSuccessHistogram); - expect(body.authenticationsFailure!).to.eql(expectedResult.authFailure); - expect(body.authenticationsFailureHistogram!).to.eql(expectedResult.authFailureHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiAuthentications, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.authenticationsSuccess).to.eql(expectedResult.authSuccess); + expect(body.authenticationsSuccessHistogram).to.eql(expectedResult.authSuccessHistogram); + expect(body.authenticationsFailure).to.eql(expectedResult.authFailure); + expect(body.authenticationsFailureHistogram).to.eql(expectedResult.authFailureHistogram); }); it('Make sure that we get KpiUniqueIps data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiUniqueIps, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['filebeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); - expect(body.uniqueDestinationIpsHistogram!).to.eql( - expectedResult.uniqueDestinationIpsHistogram - ); - expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); - expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiUniqueIps, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.uniqueDestinationIps).to.eql(expectedResult.uniqueDestinationIps); + expect(body.uniqueDestinationIpsHistogram).to.eql( + expectedResult.uniqueDestinationIpsHistogram + ); + expect(body.uniqueSourceIps).to.eql(expectedResult.uniqueSourceIps); + expect(body.uniqueSourceIpsHistogram).to.eql(expectedResult.uniqueSourceIpsHistogram); }); }); describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/kpi_hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/kpi_hosts')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/kpi_hosts') + ); + after( + async () => + await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/kpi_hosts') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -188,79 +189,69 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiHosts data', async () => { - await retry.try(async () => { - const { body: kpiHosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiHosts, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - - expect(kpiHosts.hostsHistogram!).to.eql(expectedResult.hostsHistogram); - expect(kpiHosts.hosts!).to.eql(expectedResult.hosts); + const kpiHosts = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiHosts, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(kpiHosts.hostsHistogram).to.eql(expectedResult.hostsHistogram); + expect(kpiHosts.hosts).to.eql(expectedResult.hosts); }); it('Make sure that we get KpiAuthentications data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiAuthentications, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.authenticationsSuccess!).to.eql(expectedResult.authSuccess); - expect(body.authenticationsSuccessHistogram!).to.eql(expectedResult.authSuccessHistogram); - expect(body.authenticationsFailure!).to.eql(expectedResult.authFailure); - expect(body.authenticationsFailureHistogram!).to.eql(expectedResult.authFailureHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiAuthentications, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.authenticationsSuccess).to.eql(expectedResult.authSuccess); + expect(body.authenticationsSuccessHistogram).to.eql(expectedResult.authSuccessHistogram); + expect(body.authenticationsFailure).to.eql(expectedResult.authFailure); + expect(body.authenticationsFailureHistogram).to.eql(expectedResult.authFailureHistogram); }); it('Make sure that we get KpiUniqueIps data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiUniqueIps, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); - expect(body.uniqueDestinationIpsHistogram!).to.eql( - expectedResult.uniqueDestinationIpsHistogram - ); - expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); - expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiUniqueIps, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); + expect(body.uniqueDestinationIpsHistogram!).to.eql( + expectedResult.uniqueDestinationIpsHistogram + ); + expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); + expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts index 53b099bbe18d39..637b69ff1daaae 100644 --- a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts +++ b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts @@ -7,16 +7,28 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { NetworkKpiQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + NetworkKpiDnsStrategyResponse, + NetworkKpiNetworkEventsStrategyResponse, + NetworkKpiQueries, + NetworkKpiTlsHandshakesStrategyResponse, + NetworkKpiUniqueFlowsStrategyResponse, + NetworkKpiUniquePrivateIpsStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Kpi Network', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -66,10 +78,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiNetwork uniqueFlows data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniqueFlows, timerange: { interval: '12h', @@ -79,18 +90,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueFlowId).to.eql(expectedResult.uniqueFlowId); }); it('Make sure that we get KpiNetwork networkEvents data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.networkEvents, timerange: { interval: '12h', @@ -100,18 +109,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); }); it('Make sure that we get KpiNetwork DNS data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.dns, timerange: { interval: '12h', @@ -121,18 +128,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.dnsQueries).to.eql(expectedResult.dnsQueries); }); it('Make sure that we get KpiNetwork networkEvents data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.networkEvents, timerange: { interval: '12h', @@ -142,18 +147,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); }); it('Make sure that we get KpiNetwork tlsHandshakes data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.tlsHandshakes, timerange: { interval: '12h', @@ -163,18 +166,17 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.tlsHandshakes).to.eql(expectedResult.tlsHandshakes); }); it('Make sure that we get KpiNetwork uniquePrivateIps data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniquePrivateIps, timerange: { interval: '12h', @@ -184,9 +186,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueDestinationPrivateIps).to.eql( expectedResult.uniqueDestinationPrivateIps @@ -202,8 +204,12 @@ export default function ({ getService }: FtrProviderContext) { }); describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -219,10 +225,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiNetwork uniqueFlows data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniqueFlows, timerange: { interval: '12h', @@ -232,18 +237,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueFlowId).to.eql(expectedResult.uniqueFlowId); }); it('Make sure that we get KpiNetwork DNS data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.dns, timerange: { interval: '12h', @@ -253,18 +256,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.dnsQueries).to.eql(expectedResult.dnsQueries); }); it('Make sure that we get KpiNetwork networkEvents data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.networkEvents, timerange: { interval: '12h', @@ -274,18 +275,17 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); }); it('Make sure that we get KpiNetwork tlsHandshakes data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.tlsHandshakes, timerange: { interval: '12h', @@ -295,18 +295,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.tlsHandshakes).to.eql(expectedResult.tlsHandshakes); }); it('Make sure that we get KpiNetwork uniquePrivateIps data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniquePrivateIps, timerange: { interval: '12h', @@ -316,9 +314,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueDestinationPrivateIps).to.eql( expectedResult.uniqueDestinationPrivateIps diff --git a/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts b/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts index 6040ecd1001d94..c7b6bbb84436fd 100644 --- a/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts +++ b/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts @@ -6,54 +6,43 @@ */ import expect from '@kbn/expect'; -import request from 'superagent'; import { MatrixHistogramQuery, MatrixHistogramType, + NetworkDnsStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; -/** - * Function copied from here: - * test/api_integration/apis/search/bsearch.ts - * - * Splits the JSON lines from bsearch - */ -export const parseBfetchResponse = (resp: request.Response): Array> => { - return resp.text - .trim() - .split('\n') - .map((item) => JSON.parse(item)); -}; - export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); - const retry = getService('retry'); + const bsearch = getService('bsearch'); describe('Matrix DNS Histogram', () => { describe('Large data set', () => { - before(() => - esArchiver.load( - 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' - ) + before( + async () => + await esArchiver.load( + 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' + ) ); - after(() => - esArchiver.unload( - 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' - ) + + after( + async () => + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' + ) ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; it('Make sure that we get dns data without getting bucket errors when querying large volume of data', async () => { - const { body: networkDns } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkDns = await bsearch.send({ + supertest, + options: { defaultIndex: ['large_volume_dns_data'], docValueFields: [], factoryQueryType: MatrixHistogramQuery, @@ -62,56 +51,14 @@ export default function ({ getService }: FtrProviderContext) { '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', isPtrIncluded: false, timerange: { - interval: '12h', + interval: '12', to: TO, from: FROM, }, - }) - .expect(200); - - if (networkDns.isRunning === true) { - await retry.waitForWithTimeout('bsearch to give us results', 5000, async () => { - const resp = await supertest - .post('/internal/bsearch') - .set('kbn-xsrf', 'true') - .send({ - batch: [ - { - request: { - id: networkDns.id, - defaultIndex: ['large_volume_dns_data'], - docValueFields: [], - factoryQueryType: MatrixHistogramQuery, - histogramType: MatrixHistogramType.dns, - filterQuery: - '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', - isPtrIncluded: false, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - }, - options: { - strategy: 'securitySolutionSearchStrategy', - }, - }, - ], - }); - const parsedResponse = parseBfetchResponse(resp); - // NOTE: I would like this test to be ".to.equal(6604)" but that is flakey as sometimes the query - // does not give me that exact value. It gives me failures as seen here with notes: https://github.com/elastic/kibana/issues/97365 - // I don't think this is a bug with the query but possibly a consistency view issue with interacting with the archive - // so we instead loosen this test up a bit to avoid flake. - expect(parsedResponse[0].result.rawResponse.aggregations.dns_count.value).to.be.above( - 0 - ); - return true; - }); - } else { - expect(networkDns.isRunning).to.equal(false); - expect(networkDns.rawResponse.aggregations.dns_count.value).to.equal(6604); - } + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(networkDns.rawResponse.aggregations?.dns_count).to.eql({ value: 6604 }); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/network_details.ts b/x-pack/test/api_integration/apis/security_solution/network_details.ts index 0397e7550c9352..07e526c7c4bf5d 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_details.ts @@ -6,59 +6,70 @@ */ import expect from '@kbn/expect'; -import { NetworkQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + NetworkDetailsStrategyResponse, + NetworkQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); + describe('Network details', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); it('Make sure that we get Network details data', async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const body = await bsearch.send({ + supertest, + options: { ip: '151.205.0.17', defaultIndex: ['filebeat-*'], factoryQueryType: NetworkQueries.details, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); - expect(body.networkDetails!.source!.geo!.continent_name).to.be('North America'); - expect(body.networkDetails!.source!.geo!.location!.lat!).to.be(37.751); - expect(body.networkDetails!.host.os!.platform!).to.eql(['raspbian']); + expect(body.networkDetails.source?.geo.continent_name).to.be('North America'); + expect(body.networkDetails.source?.geo.location?.lat!).to.be(37.751); + expect(body.networkDetails.host?.os?.platform).to.eql(['raspbian']); }); }); describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default') + ); it('Make sure that we get Network details data', async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const body = await bsearch.send({ + supertest, + options: { ip: '185.53.91.88', defaultIndex: ['packetbeat-*'], factoryQueryType: NetworkQueries.details, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); - expect(body.networkDetails!.host.id!).to.eql(['2ce8b1e7d69e4a1d9c6bcddc473da9d9']); - expect(body.networkDetails!.host.name!).to.eql(['zeek-sensor-amsterdam']); - expect(body.networkDetails!.host.os!.platform!).to.eql(['ubuntu']); + expect(body.networkDetails.host?.id).to.eql(['2ce8b1e7d69e4a1d9c6bcddc473da9d9']); + expect(body.networkDetails.host?.name).to.eql(['zeek-sensor-amsterdam']); + expect(body.networkDetails.host?.os?.platform!).to.eql(['ubuntu']); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/network_dns.ts b/x-pack/test/api_integration/apis/security_solution/network_dns.ts index 80660860a164bc..474c6261babf02 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_dns.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_dns.ts @@ -11,6 +11,7 @@ import { NetworkDnsEdges, Direction, NetworkDnsFields, + NetworkDnsStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -18,20 +19,24 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Network DNS', () => { describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/dns')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/dns')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/dns') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/dns') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; it('Make sure that we get Dns data and sorting by uniqueDomains ascending', async () => { - const { body: networkDns } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkDns = await bsearch.send({ + supertest, + options: { defaultIndex: ['packetbeat-*'], docValueFields: [], factoryQueryType: NetworkQueries.dns, @@ -45,9 +50,9 @@ export default function ({ getService }: FtrProviderContext) { to: TO, from: FROM, }, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkDns.edges.length).to.be(10); expect(networkDns.totalCount).to.be(44); @@ -58,10 +63,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that we get Dns data and sorting by uniqueDomains descending', async () => { - const { body: networkDns } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkDns = await bsearch.send({ + supertest, + options: { ip: '151.205.0.17', defaultIndex: ['packetbeat-*'], factoryQueryType: NetworkQueries.dns, @@ -80,9 +84,9 @@ export default function ({ getService }: FtrProviderContext) { to: TO, from: FROM, }, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkDns.edges.length).to.be(10); expect(networkDns.totalCount).to.be(44); diff --git a/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts b/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts index af8e5439074921..41b083ab90578c 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts @@ -12,6 +12,7 @@ import { Direction, FlowTargetSourceDest, NetworkTopTablesFields, + NetworkTopNFlowStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -21,20 +22,24 @@ const EDGE_LENGTH = 10; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Network Top N Flow', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2019-02-09T01:57:24.870Z'; const TO = '2019-02-12T01:57:24.870Z'; it('Make sure that we get Source NetworkTopNFlow data with bytes_in descending sort', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: NetworkQueries.topNFlow, flowTarget: FlowTargetSourceDest.source, @@ -52,9 +57,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(121); @@ -70,10 +75,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that we get Source NetworkTopNFlow data with bytes_in ascending sort ', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: 'topNFlow', filterQuery: @@ -93,9 +97,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(121); @@ -111,10 +115,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that we get Destination NetworkTopNFlow data', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: 'topNFlow', filterQuery: @@ -134,9 +137,10 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(154); expect(networkTopNFlow.edges[0].node.destination!.flows).to.be(19); @@ -146,10 +150,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that pagination is working in NetworkTopNFlow query', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: 'topNFlow', filterQuery: @@ -169,9 +172,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(121); diff --git a/x-pack/test/api_integration/apis/security_solution/overview_host.ts b/x-pack/test/api_integration/apis/security_solution/overview_host.ts index 09bd09782d2f26..b2a32ac46c3f31 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_host.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_host.ts @@ -8,16 +8,24 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostsQueries, + HostsOverviewStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Overview Host', () => { describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -41,12 +49,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewHost data', async () => { - const { - body: { overviewHost }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewHost } = await bsearch.send({ + supertest, + options: { defaultIndex: ['auditbeat-*'], factoryQueryType: HostsQueries.overview, timerange: { @@ -56,9 +61,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewHost).to.eql(expectedResult); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/overview_network.ts b/x-pack/test/api_integration/apis/security_solution/overview_network.ts index 00adc903d5733d..ffee9b851ffc0d 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_network.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_network.ts @@ -7,16 +7,24 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { NetworkQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + NetworkOverviewStrategyResponse, + NetworkQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Overview Network', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -34,12 +42,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewNetwork data', async () => { - const { - body: { overviewNetwork }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewNetwork } = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: NetworkQueries.overview, timerange: { @@ -49,16 +54,21 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewNetwork).to.eql(expectedResult); }); }); describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/overview')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/overview')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/overview') + ); + after( + async () => + await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/overview') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -75,12 +85,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewNetwork data', async () => { - const { - body: { overviewNetwork }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewNetwork } = await bsearch.send({ + supertest, + options: { defaultIndex: ['packetbeat-*'], factoryQueryType: NetworkQueries.overview, timerange: { @@ -90,17 +97,20 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewNetwork).to.eql(expectedResult); }); }); describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -117,12 +127,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewNetwork data', async () => { - const { - body: { overviewNetwork }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewNetwork } = await bsearch.send({ + supertest, + options: { defaultIndex: ['auditbeat-*'], factoryQueryType: NetworkQueries.overview, timerange: { @@ -132,9 +139,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewNetwork).to.eql(expectedResult); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/timeline_details.ts b/x-pack/test/api_integration/apis/security_solution/timeline_details.ts index 3aefd9f8b597ab..9f1b5fccd9d1af 100644 --- a/x-pack/test/api_integration/apis/security_solution/timeline_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/timeline_details.ts @@ -7,7 +7,11 @@ import expect from '@kbn/expect'; import { sortBy } from 'lodash'; -import { TimelineEventsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + TimelineEventsQueries, + TimelineEventsDetailsStrategyResponse, + TimelineKpiStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -668,54 +672,49 @@ const EXPECTED_KPI_COUNTS = { }; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Timeline Details', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); it('Make sure that we get Event Details data', async () => { - await retry.try(async () => { - const { - body: { data: detailsData }, - } = await supertest - .post('/internal/search/timelineSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: TimelineEventsQueries.details, - docValueFields: [], - indexName: INDEX_NAME, - inspect: false, - eventId: ID, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(sortBy(detailsData, 'field')).to.eql(sortBy(EXPECTED_DATA, 'field')); + const { data: detailsData } = await bsearch.send({ + supertest, + options: { + factoryQueryType: TimelineEventsQueries.details, + docValueFields: [], + indexName: INDEX_NAME, + inspect: false, + eventId: ID, + }, + strategy: 'timelineSearchStrategy', }); + expect(sortBy(detailsData, 'field')).to.eql(sortBy(EXPECTED_DATA, 'field')); }); it('Make sure that we get kpi data', async () => { - await retry.try(async () => { - const { - body: { destinationIpCount, hostCount, processCount, sourceIpCount, userCount }, - } = await supertest - .post('/internal/search/timelineSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { destinationIpCount, hostCount, processCount, sourceIpCount, userCount } = + await bsearch.send({ + supertest, + options: { factoryQueryType: TimelineEventsQueries.kpi, docValueFields: [], indexName: INDEX_NAME, inspect: false, eventId: ID, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect({ destinationIpCount, hostCount, processCount, sourceIpCount, userCount }).to.eql( - EXPECTED_KPI_COUNTS - ); - }); + }, + strategy: 'timelineSearchStrategy', + }); + expect({ destinationIpCount, hostCount, processCount, sourceIpCount, userCount }).to.eql( + EXPECTED_KPI_COUNTS + ); }); }); } diff --git a/x-pack/test/api_integration/apis/security_solution/tls.ts b/x-pack/test/api_integration/apis/security_solution/tls.ts index 9fa251ded4e6b1..c872844cb3d463 100644 --- a/x-pack/test/api_integration/apis/security_solution/tls.ts +++ b/x-pack/test/api_integration/apis/security_solution/tls.ts @@ -11,6 +11,7 @@ import { Direction, NetworkTlsFields, FlowTarget, + NetworkTlsStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -83,17 +84,21 @@ const expectedOverviewSourceResult = { export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Tls Test with Packetbeat', () => { describe('Tls Test', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls') + ); it('Ensure data is returned for FlowTarget.Source', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -112,19 +117,18 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(tls.edges.length).to.be(1); expect(tls.totalCount).to.be(1); expect(tls.edges[0].node).to.eql(expectedResult); }); it('Ensure data is returned for FlowTarget.Destination', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -143,9 +147,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(tls.edges.length).to.be(1); expect(tls.totalCount).to.be(1); expect(tls.edges[0].node).to.eql(expectedResult); @@ -153,14 +157,17 @@ export default function ({ getService }: FtrProviderContext) { }); describe('Tls Overview Test', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls') + ); it('Ensure data is returned for FlowTarget.Source', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -179,18 +186,18 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(tls.pageInfo).to.eql(expectedOverviewSourceResult.pageInfo); expect(tls.edges[0]).to.eql(expectedOverviewSourceResult.edges[0]); }); it('Ensure data is returned for FlowTarget.Destination', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -209,9 +216,10 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(tls.pageInfo).to.eql(expectedOverviewDestinationResult.pageInfo); expect(tls.edges[0]).to.eql(expectedOverviewDestinationResult.edges[0]); }); diff --git a/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts b/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts index d39cc0afb6461a..a6749b27030e1b 100644 --- a/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts +++ b/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts @@ -13,10 +13,6 @@ import { } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; -interface UncommonProcessesResponse { - body: HostsUncommonProcessesStrategyResponse; -} - const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -24,24 +20,51 @@ const TO = '3000-01-01T00:00:00.000Z'; const TOTAL_COUNT = 3; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('uncommon_processes', () => { - before(() => - esArchiver.load('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') + before( + async () => + await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') ); - after(() => - esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') + after( + async () => + await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') ); it('should return an edge of length 1 when given a pagination of length 1', async () => { - await retry.try(async () => { - const response = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const response = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsQueries.uncommonProcesses, + sourceId: 'default', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 3, + querySize: 1, + }, + defaultIndex: ['auditbeat-uncommon-processes'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(response.edges.length).to.be(1); + }); + + describe('when given a pagination of length 2', () => { + it('should return an edge of length 2 ', async () => { + const response = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.uncommonProcesses, sourceId: 'default', timerange: { @@ -53,84 +76,51 @@ export default function ({ getService }: FtrProviderContext) { activePage: 0, cursorStart: 0, fakePossibleCount: 3, - querySize: 1, + querySize: 2, }, defaultIndex: ['auditbeat-uncommon-processes'], docValueFields: [], inspect: false, - }) - .expect(200); - expect(response!.body.edges.length).to.be(1); - }); - }); - - describe('when given a pagination of length 2', () => { - it('should return an edge of length 2 ', async () => { - await retry.try(async () => { - const response = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsQueries.uncommonProcesses, - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 3, - querySize: 2, - }, - defaultIndex: ['auditbeat-uncommon-processes'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(response!.body.edges.length).to.be(2); + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(response.edges.length).to.be(2); }); }); describe('when given a pagination of length 1', () => { - let response: null | UncommonProcessesResponse = null; + let response: HostsUncommonProcessesStrategyResponse | null = null; before(async () => { - await retry.try(async () => { - response = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsQueries.uncommonProcesses, - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 3, - querySize: 1, - }, - defaultIndex: ['auditbeat-uncommon-processes'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + response = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsQueries.uncommonProcesses, + sourceId: 'default', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 3, + querySize: 1, + }, + defaultIndex: ['auditbeat-uncommon-processes'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); }); it('should return an edge of length 1 ', () => { - expect(response!.body.edges.length).to.be(1); + expect(response?.edges.length).to.be(1); }); it('should return a total count of elements', () => { - expect(response!.body.totalCount).to.be(TOTAL_COUNT); + expect(response?.totalCount).to.be(TOTAL_COUNT); }); it('should return a single data set with pagination of 1', () => { @@ -152,7 +142,7 @@ export default function ({ getService }: FtrProviderContext) { }, ], }; - expect(response!.body.edges[0].node).to.eql(expected); + expect(response?.edges[0].node).to.eql(expected); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/users.ts b/x-pack/test/api_integration/apis/security_solution/users.ts index 84335cc2695ce1..d592c99bf006f1 100644 --- a/x-pack/test/api_integration/apis/security_solution/users.ts +++ b/x-pack/test/api_integration/apis/security_solution/users.ts @@ -11,6 +11,7 @@ import { Direction, NetworkUsersFields, FlowTarget, + NetworkUsersStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -20,53 +21,52 @@ const TO = '3000-01-01T00:00:00.000Z'; const IP = '0.0.0.0'; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); + describe('Users', () => { describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/users')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/users')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/users') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/users') + ); it('Ensure data is returned from auditbeat', async () => { - await retry.try(async () => { - const { body: users } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: NetworkQueries.users, - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-users'], - docValueFields: [], - ip: IP, - flowTarget: FlowTarget.destination, - sort: { field: NetworkUsersFields.name, direction: Direction.asc }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 30, - querySize: 10, - }, - inspect: false, - /* We need a very long timeout to avoid returning just partial data. - ** https://github.com/elastic/kibana/blob/master/x-pack/test/api_integration/apis/search/search.ts#L18 - */ - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(users.edges.length).to.be(1); - expect(users.totalCount).to.be(1); - expect(users.edges[0].node.user!.id).to.eql(['0']); - expect(users.edges[0].node.user!.name).to.be('root'); - expect(users.edges[0].node.user!.groupId).to.eql(['0']); - expect(users.edges[0].node.user!.groupName).to.eql(['root']); - expect(users.edges[0].node.user!.count).to.be(1); + const users = await bsearch.send({ + supertest, + options: { + factoryQueryType: NetworkQueries.users, + sourceId: 'default', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-users'], + docValueFields: [], + ip: IP, + flowTarget: FlowTarget.destination, + sort: { field: NetworkUsersFields.name, direction: Direction.asc }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 30, + querySize: 10, + }, + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(users.edges.length).to.be(1); + expect(users.totalCount).to.be(1); + expect(users.edges[0].node.user?.id).to.eql(['0']); + expect(users.edges[0].node.user?.name).to.be('root'); + expect(users.edges[0].node.user?.groupId).to.eql(['0']); + expect(users.edges[0].node.user?.groupName).to.eql(['root']); + expect(users.edges[0].node.user?.count).to.be(1); }); }); }); diff --git a/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts b/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts index e44f29c41640fe..1e9cfe3eb841b3 100644 --- a/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts +++ b/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts @@ -135,6 +135,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should be able to view alerts from "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); const resp = await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) @@ -164,6 +166,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should NOT be able to view alerts from "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); const resp = await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) @@ -183,6 +187,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should NOT be able to access "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) diff --git a/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts b/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts index 0a73009196bafd..91ad87737805f7 100644 --- a/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts +++ b/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts @@ -117,6 +117,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should be able to view alerts from "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); const resp = await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) @@ -145,6 +147,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should NOT be able to access "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) From 9978803893398b67e95d6b8ad64f592beed99131 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 27 Oct 2021 21:24:35 +0100 Subject: [PATCH 19/46] chore(NA): adds backport config for 8.1.0 bump (#116431) --- .backportrc.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.backportrc.json b/.backportrc.json index 89eefb2e3c4420..24e08d4768d9af 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -2,6 +2,7 @@ "upstream": "elastic/kibana", "targetBranchChoices": [ { "name": "master", "checked": true }, + "8.0", "7.16", "7.15", "7.14", @@ -32,7 +33,7 @@ ], "targetPRLabels": ["backport"], "branchLabelMapping": { - "^v8.0.0$": "master", + "^v8.1.0$": "master", "^v(\\d+).(\\d+).\\d+$": "$1.$2" }, "autoMerge": true, From edc8f9583bdf0e8d7e7cee1823af1c667ebc2b1e Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 27 Oct 2021 21:24:48 +0100 Subject: [PATCH 20/46] chore(NA): bump version to 8.1.0 (#116429) * chore(NA): bump version to 8.1.0 * chore(NA): update non supported plugins * chore(NA): update non supported plugins * chore(NA): skip failing tests * chore(NA): update jest snapshots Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 2 +- .../integration_tests/corrupt_outdated_docs.test.ts | 2 +- .../migrationsv2/integration_tests/outdated_docs.test.ts | 2 +- x-pack/package.json | 2 +- x-pack/plugins/file_upload/kibana.json | 2 +- x-pack/plugins/global_search_bar/kibana.json | 2 +- x-pack/plugins/graph/kibana.json | 4 ++-- x-pack/plugins/grokdebugger/kibana.json | 2 +- x-pack/plugins/index_management/common/constants/plugin.ts | 2 +- x-pack/plugins/ingest_pipelines/kibana.json | 2 +- .../analytics_list/__mocks__/analytics_list_item.json | 2 +- x-pack/plugins/painless_lab/kibana.json | 2 +- x-pack/plugins/remote_clusters/common/constants.ts | 2 +- x-pack/plugins/stack_alerts/kibana.json | 2 +- x-pack/plugins/task_manager/kibana.json | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 3c42c077ae5b02..0e5c042e4236d4 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dashboarding" ], "private": true, - "version": "8.0.0", + "version": "8.1.0", "branch": "master", "types": "./kibana.d.ts", "tsdocMetadata": "./build/tsdoc-metadata.json", diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts index 348cbe88cd8a7a..ebc632f059ced2 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts @@ -40,7 +40,7 @@ describe('migration v2 with corrupt saved object documents', () => { await new Promise((resolve) => setTimeout(resolve, 10000)); }); - it('collects corrupt saved object documents across batches', async () => { + it.skip('collects corrupt saved object documents across batches', async () => { const { startES } = kbnTestServer.createTestServers({ adjustTimeout: (t: number) => jest.setTimeout(t), settings: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts index 506f42cb2e402e..9fd8f3884c4ae7 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts @@ -41,7 +41,7 @@ describe('migration v2', () => { await new Promise((resolve) => setTimeout(resolve, 10000)); }); - it('migrates the documents to the highest version', async () => { + it.skip('migrates the documents to the highest version', async () => { const migratedIndex = `.kibana_${pkg.version}_001`; const { startES } = kbnTestServer.createTestServers({ adjustTimeout: (t: number) => jest.setTimeout(t), diff --git a/x-pack/package.json b/x-pack/package.json index 805d8555bf453f..8fb7a3483e5ef0 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -1,6 +1,6 @@ { "name": "x-pack", - "version": "8.0.0", + "version": "8.1.0", "author": "Elastic", "private": true, "license": "Elastic-License", diff --git a/x-pack/plugins/file_upload/kibana.json b/x-pack/plugins/file_upload/kibana.json index e69c5e34bc09bf..f70bc6377972a1 100644 --- a/x-pack/plugins/file_upload/kibana.json +++ b/x-pack/plugins/file_upload/kibana.json @@ -1,6 +1,6 @@ { "id": "fileUpload", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "server": true, "ui": true, diff --git a/x-pack/plugins/global_search_bar/kibana.json b/x-pack/plugins/global_search_bar/kibana.json index 5c0a9999b8e3a5..94b8aba2dd7f9f 100644 --- a/x-pack/plugins/global_search_bar/kibana.json +++ b/x-pack/plugins/global_search_bar/kibana.json @@ -4,7 +4,7 @@ "name": "Kibana Core", "githubTeam": "kibana-core" }, - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "server": false, "ui": true, diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json index 410a5e2c160d6f..03729c706df25d 100644 --- a/x-pack/plugins/graph/kibana.json +++ b/x-pack/plugins/graph/kibana.json @@ -1,6 +1,6 @@ { "id": "graph", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "server": true, "ui": true, @@ -29,4 +29,4 @@ "name": "Data Discovery", "githubTeam": "kibana-data-discovery" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/grokdebugger/kibana.json b/x-pack/plugins/grokdebugger/kibana.json index 692aa16329d549..f8cb75f7d10b66 100644 --- a/x-pack/plugins/grokdebugger/kibana.json +++ b/x-pack/plugins/grokdebugger/kibana.json @@ -1,6 +1,6 @@ { "id": "grokdebugger", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "owner": { "name": "Stack Management", diff --git a/x-pack/plugins/index_management/common/constants/plugin.ts b/x-pack/plugins/index_management/common/constants/plugin.ts index ad57398000426b..060d42ca26b023 100644 --- a/x-pack/plugins/index_management/common/constants/plugin.ts +++ b/x-pack/plugins/index_management/common/constants/plugin.ts @@ -22,4 +22,4 @@ export const PLUGIN = { // "PluginInitializerContext.env.packageInfo.version". In some cases it is not possible // to dynamically inject that version without a huge refactor on the code base. // We will then keep this single constant to declare on which major branch we are. -export const MAJOR_VERSION = '8.0.0'; +export const MAJOR_VERSION = '8.1.0'; diff --git a/x-pack/plugins/ingest_pipelines/kibana.json b/x-pack/plugins/ingest_pipelines/kibana.json index 800d92b5c97488..889559826f1f1d 100644 --- a/x-pack/plugins/ingest_pipelines/kibana.json +++ b/x-pack/plugins/ingest_pipelines/kibana.json @@ -1,6 +1,6 @@ { "id": "ingestPipelines", - "version": "8.0.0", + "version": "8.1.0", "server": true, "ui": true, "owner": { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json index 20343755f7f0f1..1b7d353d9f3038 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json @@ -22,7 +22,7 @@ }, "model_memory_limit": "50mb", "create_time": 1568974998023, - "version": "8.0.0" + "version": "8.1.0" }, "id": "fq_outlier_1222", "checkpointing": {}, diff --git a/x-pack/plugins/painless_lab/kibana.json b/x-pack/plugins/painless_lab/kibana.json index 7c71d4bdb4b761..1f59bf30bb761f 100644 --- a/x-pack/plugins/painless_lab/kibana.json +++ b/x-pack/plugins/painless_lab/kibana.json @@ -1,6 +1,6 @@ { "id": "painlessLab", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "owner": { "name": "Stack Management", diff --git a/x-pack/plugins/remote_clusters/common/constants.ts b/x-pack/plugins/remote_clusters/common/constants.ts index 5a36924b26433c..072d6d437b8b99 100644 --- a/x-pack/plugins/remote_clusters/common/constants.ts +++ b/x-pack/plugins/remote_clusters/common/constants.ts @@ -20,7 +20,7 @@ export const PLUGIN = { }, }; -export const MAJOR_VERSION = '8.0.0'; +export const MAJOR_VERSION = '8.1.0'; export const API_BASE_PATH = '/api/remote_clusters'; diff --git a/x-pack/plugins/stack_alerts/kibana.json b/x-pack/plugins/stack_alerts/kibana.json index 1b4271328c2f95..693bcf2f8dbca0 100644 --- a/x-pack/plugins/stack_alerts/kibana.json +++ b/x-pack/plugins/stack_alerts/kibana.json @@ -5,7 +5,7 @@ "githubTeam": "kibana-alerting-services" }, "server": true, - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "requiredPlugins": [ "alerting", diff --git a/x-pack/plugins/task_manager/kibana.json b/x-pack/plugins/task_manager/kibana.json index d0b847ce58d77b..36e68ca00af816 100644 --- a/x-pack/plugins/task_manager/kibana.json +++ b/x-pack/plugins/task_manager/kibana.json @@ -1,7 +1,7 @@ { "id": "taskManager", "server": true, - "version": "8.0.0", + "version": "8.1.0", "owner": { "name": "Kibana Alerting", "githubTeam": "kibana-alerting-services" From dc2bea778cd7561794b15a763ad4061b8d996f13 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Wed, 27 Oct 2021 16:43:00 -0400 Subject: [PATCH 21/46] Lowered bundle size limits for dashboard and presentationUtil (#116478) --- packages/kbn-optimizer/limits.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 453e5c174452d0..99f9c04069b720 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -8,7 +8,7 @@ pageLoadAssetSize: console: 46091 core: 435325 crossClusterReplication: 65408 - dashboard: 186763 + dashboard: 82025 dashboardEnhanced: 65646 devTools: 38637 discover: 99999 @@ -76,7 +76,7 @@ pageLoadAssetSize: watcher: 43598 runtimeFields: 41752 stackAlerts: 29684 - presentationUtil: 94301 + presentationUtil: 84606 osquery: 107090 fileUpload: 25664 dataVisualizer: 27530 From 83288af8bd200e607b31d7c50253786a9d1a9382 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 27 Oct 2021 21:34:41 +0000 Subject: [PATCH 22/46] skip failing suite (#72874) --- x-pack/test/security_solution_endpoint/apps/endpoint/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts index 70d60ba5c1b67f..d1cfddbca3a9c7 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts @@ -15,7 +15,8 @@ import { export default function (providerContext: FtrProviderContext) { const { loadTestFile, getService } = providerContext; - describe('endpoint', function () { + // FAILING: https://github.com/elastic/kibana/issues/72874 + describe.skip('endpoint', function () { const ingestManager = getService('ingestManager'); const log = getService('log'); const endpointTestResources = getService('endpointTestResources'); From 17df3a265472a2a666d07b07b8a00a31702efc31 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 27 Oct 2021 17:03:37 -0500 Subject: [PATCH 23/46] [ci] bump timeout for cigroups until we figure out the slowdown (#116506) Co-authored-by: spalger --- .buildkite/pipelines/hourly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 3337cfb5dfcdd7..b03a46b5b5c667 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -21,7 +21,7 @@ steps: agents: queue: ci-group-6 depends_on: build - timeout_in_minutes: 150 + timeout_in_minutes: 250 key: default-cigroup retry: automatic: From 4efabacd4ef59128582089bff1f594658dbf40ed Mon Sep 17 00:00:00 2001 From: "Joey F. Poon" Date: Wed, 27 Oct 2021 17:38:53 -0500 Subject: [PATCH 24/46] [Security Solution] fix endpoint being visible after unenrolling agent (#116372) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/fleet/server/services/agents/helpers.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/fleet/server/services/agents/helpers.ts b/x-pack/plugins/fleet/server/services/agents/helpers.ts index 609d5ba6c83a05..a4a7803d35e2cb 100644 --- a/x-pack/plugins/fleet/server/services/agents/helpers.ts +++ b/x-pack/plugins/fleet/server/services/agents/helpers.ts @@ -42,5 +42,9 @@ export function agentSOAttributesToFleetServerAgentDoc( doc.policy_revision_idx = policyRevison; } + if (!doc.updated_at) { + doc.updated_at = new Date().toISOString(); + } + return doc; } From ede3882e1857df38cbd5595de3916f435614d1b0 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 27 Oct 2021 20:55:23 -0400 Subject: [PATCH 25/46] [Fleet] Fix back button when adding integration from multiple integrations package (#116352) --- .../integrations/sections/epm/screens/detail/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 6e3eba19c52e3d..c8c6f493568109 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -271,6 +271,7 @@ export function Detail() { { path: pagePathGetters.integration_details_policies({ pkgkey, + ...(integration ? { integration } : {}), })[1], }, ]; @@ -289,6 +290,7 @@ export function Detail() { { path: pagePathGetters.integration_details_overview({ pkgkey, + ...(integration ? { integration } : {}), })[1], }, ], From 9e6e84571f92b4602681ea0f9171f5b8cc0643d8 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 27 Oct 2021 20:38:43 -0500 Subject: [PATCH 26/46] Revert "[Canvas] By-Value Embeddables (#113827)" (#116527) This reverts commit bacd7f91dd8e2904c9b88523ddb46edd1d41fe2e. Co-authored-by: spalger --- .../application/top_nav/editor_menu.tsx | 2 +- .../server/collectors/management/schema.ts | 4 - .../server/collectors/management/types.ts | 1 - src/plugins/presentation_util/common/labs.ts | 17 +- .../solution_toolbar/items/quick_group.scss | 34 +--- src/plugins/telemetry/schema/oss_plugins.json | 6 - .../expression_types/embeddable.ts | 3 +- .../functions/browser/index.ts | 12 +- .../functions/external/embeddable.test.ts | 60 ------- .../functions/external/embeddable.ts | 145 --------------- .../functions/external/index.ts | 19 +- .../functions/external/saved_lens.ts | 16 +- .../functions/external/saved_map.ts | 5 +- .../functions/external/saved_visualization.ts | 3 +- .../canvas/canvas_plugin_src/plugin.ts | 10 -- .../renderers/embeddable/embeddable.tsx | 56 +++--- .../embeddable_input_to_expression.ts | 8 +- .../embeddable.test.ts | 128 ------------- .../input_type_to_expression/embeddable.ts | 13 -- .../input_type_to_expression/lens.test.ts | 5 +- .../input_type_to_expression/lens.ts | 2 +- .../input_type_to_expression/map.test.ts | 12 +- .../input_type_to_expression/map.ts | 7 +- .../visualization.test.ts | 5 +- .../input_type_to_expression/visualization.ts | 4 +- .../canvas/common/lib/embeddable_dataurl.ts | 13 -- .../canvas/i18n/functions/dict/embeddable.ts | 25 --- .../canvas/i18n/functions/function_help.ts | 2 - x-pack/plugins/canvas/kibana.json | 1 - .../embeddable_flyout/flyout.component.tsx | 44 ++--- .../components/embeddable_flyout/flyout.tsx | 21 +-- .../public/components/hooks/workpad/index.tsx | 2 - .../hooks/workpad/use_incoming_embeddable.ts | 86 --------- .../public/components/workpad/workpad.tsx | 4 - .../components/workpad_app/workpad_app.scss | 2 +- .../editor_menu.stories.storyshot | 81 --------- .../__stories__/editor_menu.stories.tsx | 107 ----------- .../editor_menu/editor_menu.component.tsx | 170 ------------------ .../editor_menu/editor_menu.tsx | 147 --------------- .../workpad_header/editor_menu/index.ts | 9 - .../element_menu/element_menu.component.tsx | 6 +- .../workpad_header/element_menu/index.ts | 3 +- .../workpad_header.component.tsx | 28 +-- x-pack/plugins/canvas/public/plugin.tsx | 9 +- .../routes/workpad/hooks/use_workpad.ts | 18 +- .../canvas/public/services/embeddables.ts | 6 +- .../plugins/canvas/public/services/index.ts | 4 - .../public/services/kibana/embeddables.ts | 1 - .../canvas/public/services/kibana/index.ts | 3 - .../public/services/kibana/visualizations.ts | 21 --- .../public/services/stubs/embeddables.ts | 1 - .../canvas/public/services/stubs/index.ts | 3 - .../public/services/stubs/visualizations.ts | 19 -- .../canvas/public/services/visualizations.ts | 14 -- .../public/state/reducers/embeddable.ts | 2 +- x-pack/plugins/canvas/server/plugin.ts | 9 +- .../canvas/server/setup_interpreter.ts | 12 +- x-pack/plugins/canvas/types/embeddables.ts | 16 -- x-pack/plugins/canvas/types/functions.ts | 10 +- x-pack/plugins/canvas/types/index.ts | 1 - 60 files changed, 131 insertions(+), 1346 deletions(-) delete mode 100644 x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts delete mode 100644 x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts delete mode 100644 x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.test.ts delete mode 100644 x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts delete mode 100644 x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts delete mode 100644 x-pack/plugins/canvas/i18n/functions/dict/embeddable.ts delete mode 100644 x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/editor_menu/index.ts delete mode 100644 x-pack/plugins/canvas/public/services/kibana/visualizations.ts delete mode 100644 x-pack/plugins/canvas/public/services/stubs/visualizations.ts delete mode 100644 x-pack/plugins/canvas/public/services/visualizations.ts delete mode 100644 x-pack/plugins/canvas/types/embeddables.ts diff --git a/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx b/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx index effbf8ce980d73..8a46a16c1bf0cf 100644 --- a/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx +++ b/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx @@ -231,7 +231,7 @@ export const EditorMenu = ({ dashboardContainer, createNewVisType }: Props) => { = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, - 'labs:canvas:byValueEmbeddable': { - type: 'boolean', - _meta: { description: 'Non-default value of setting.' }, - }, 'labs:canvas:useDataService': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 53d651b022529f..9dcd2038edb9d7 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -121,7 +121,6 @@ export interface UsageStats { 'banners:textColor': string; 'banners:backgroundColor': string; 'labs:canvas:enable_ui': boolean; - 'labs:canvas:byValueEmbeddable': boolean; 'labs:canvas:useDataService': boolean; 'labs:presentation:timeToPresent': boolean; 'labs:dashboard:enable_ui': boolean; diff --git a/src/plugins/presentation_util/common/labs.ts b/src/plugins/presentation_util/common/labs.ts index 8eefbd6981280c..cb976e73b5edfe 100644 --- a/src/plugins/presentation_util/common/labs.ts +++ b/src/plugins/presentation_util/common/labs.ts @@ -11,9 +11,7 @@ import { i18n } from '@kbn/i18n'; export const LABS_PROJECT_PREFIX = 'labs:'; export const DEFER_BELOW_FOLD = `${LABS_PROJECT_PREFIX}dashboard:deferBelowFold` as const; export const DASHBOARD_CONTROLS = `${LABS_PROJECT_PREFIX}dashboard:dashboardControls` as const; -export const BY_VALUE_EMBEDDABLE = `${LABS_PROJECT_PREFIX}canvas:byValueEmbeddable` as const; - -export const projectIDs = [DEFER_BELOW_FOLD, DASHBOARD_CONTROLS, BY_VALUE_EMBEDDABLE] as const; +export const projectIDs = [DEFER_BELOW_FOLD, DASHBOARD_CONTROLS] as const; export const environmentNames = ['kibana', 'browser', 'session'] as const; export const solutionNames = ['canvas', 'dashboard', 'presentation'] as const; @@ -50,19 +48,6 @@ export const projects: { [ID in ProjectID]: ProjectConfig & { id: ID } } = { }), solutions: ['dashboard'], }, - [BY_VALUE_EMBEDDABLE]: { - id: BY_VALUE_EMBEDDABLE, - isActive: true, - isDisplayed: true, - environments: ['kibana', 'browser', 'session'], - name: i18n.translate('presentationUtil.labs.enableByValueEmbeddableName', { - defaultMessage: 'By-Value Embeddables', - }), - description: i18n.translate('presentationUtil.labs.enableByValueEmbeddableDescription', { - defaultMessage: 'Enables support for by-value embeddables in Canvas', - }), - solutions: ['canvas'], - }, }; export type ProjectID = typeof projectIDs[number]; diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss b/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss index c70e317546d403..535570a51d777c 100644 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss +++ b/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss @@ -1,31 +1,11 @@ .quickButtonGroup { - .euiButtonGroup__buttons { - border-radius: $euiBorderRadius; - - .quickButtonGroup__button { - background-color: $euiColorEmptyShade; - @include kbnThemeStyle('v8') { - // sass-lint:disable-block no-important - border-width: $euiBorderWidthThin !important; - border-style: solid !important; - border-color: $euiBorderColor !important; - } - } - - .quickButtonGroup__button:first-of-type { - @include kbnThemeStyle('v8') { - // sass-lint:disable-block no-important - border-top-left-radius: $euiBorderRadius !important; - border-bottom-left-radius: $euiBorderRadius !important; - } - } - - .quickButtonGroup__button:last-of-type { - @include kbnThemeStyle('v8') { - // sass-lint:disable-block no-important - border-top-right-radius: $euiBorderRadius !important; - border-bottom-right-radius: $euiBorderRadius !important; - } + .quickButtonGroup__button { + background-color: $euiColorEmptyShade; + @include kbnThemeStyle('v8') { + // sass-lint:disable-block no-important + border-width: $euiBorderWidthThin !important; + border-style: solid !important; + border-color: $euiBorderColor !important; } } } diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index e7c18966ce0c09..251fed955788eb 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7677,12 +7677,6 @@ "description": "Non-default value of setting." } }, - "labs:canvas:byValueEmbeddable": { - "type": "boolean", - "_meta": { - "description": "Non-default value of setting." - } - }, "labs:canvas:useDataService": { "type": "boolean", "_meta": { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts index f1ede936c6ace4..ac2e8e8babee1e 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts @@ -6,11 +6,12 @@ */ import { ExpressionTypeDefinition } from '../../../../../src/plugins/expressions'; -import { EmbeddableInput } from '../../types'; +import { EmbeddableInput } from '../../../../../src/plugins/embeddable/common/'; import { EmbeddableTypes } from './embeddable_types'; export const EmbeddableExpressionType = 'embeddable'; export { EmbeddableTypes, EmbeddableInput }; + export interface EmbeddableExpression { /** * The type of the expression result diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts index d6d7a0f867849c..2cfdebafb70df4 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts @@ -6,6 +6,7 @@ */ import { functions as commonFunctions } from '../common'; +import { functions as externalFunctions } from '../external'; import { location } from './location'; import { markdown } from './markdown'; import { urlparam } from './urlparam'; @@ -13,4 +14,13 @@ import { escount } from './escount'; import { esdocs } from './esdocs'; import { essql } from './essql'; -export const functions = [location, markdown, urlparam, escount, esdocs, essql, ...commonFunctions]; +export const functions = [ + location, + markdown, + urlparam, + escount, + esdocs, + essql, + ...commonFunctions, + ...externalFunctions, +]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts deleted file mode 100644 index 001fb0e3f62e3a..00000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { embeddableFunctionFactory } from './embeddable'; -import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; -import { ExpressionValueFilter } from '../../../types'; -import { encode } from '../../../common/lib/embeddable_dataurl'; -import { InitializeArguments } from '.'; - -const filterContext: ExpressionValueFilter = { - type: 'filter', - and: [ - { - type: 'filter', - and: [], - value: 'filter-value', - column: 'filter-column', - filterType: 'exactly', - }, - { - type: 'filter', - and: [], - column: 'time-column', - filterType: 'time', - from: '2019-06-04T04:00:00.000Z', - to: '2019-06-05T04:00:00.000Z', - }, - ], -}; - -describe('embeddable', () => { - const fn = embeddableFunctionFactory({} as InitializeArguments)().fn; - const config = { - id: 'some-id', - timerange: { from: '15m', to: 'now' }, - title: 'test embeddable', - }; - - const args = { - config: encode(config), - type: 'visualization', - }; - - it('accepts null context', () => { - const expression = fn(null, args, {} as any); - - expect(expression.input.filters).toEqual([]); - }); - - it('accepts filter context', () => { - const expression = fn(filterContext, args, {} as any); - const embeddableFilters = getQueryFilters(filterContext.and); - - expect(expression.input.filters).toEqual(embeddableFilters); - }); -}); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts deleted file mode 100644 index 7ef8f0a09eb907..00000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; -import { ExpressionValueFilter, EmbeddableInput } from '../../../types'; -import { EmbeddableExpressionType, EmbeddableExpression } from '../../expression_types'; -import { getFunctionHelp } from '../../../i18n'; -import { SavedObjectReference } from '../../../../../../src/core/types'; -import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; -import { decode, encode } from '../../../common/lib/embeddable_dataurl'; -import { InitializeArguments } from '.'; - -export interface Arguments { - config: string; - type: string; -} - -const defaultTimeRange = { - from: 'now-15m', - to: 'now', -}; - -const baseEmbeddableInput = { - timeRange: defaultTimeRange, - disableTriggers: true, - renderMode: 'noInteractivity', -}; - -type Return = EmbeddableExpression; - -type EmbeddableFunction = ExpressionFunctionDefinition< - 'embeddable', - ExpressionValueFilter | null, - Arguments, - Return ->; - -export function embeddableFunctionFactory({ - embeddablePersistableStateService, -}: InitializeArguments): () => EmbeddableFunction { - return function embeddable(): EmbeddableFunction { - const { help, args: argHelp } = getFunctionHelp().embeddable; - - return { - name: 'embeddable', - help, - args: { - config: { - aliases: ['_'], - types: ['string'], - required: true, - help: argHelp.config, - }, - type: { - types: ['string'], - required: true, - help: argHelp.type, - }, - }, - context: { - types: ['filter'], - }, - type: EmbeddableExpressionType, - fn: (input, args) => { - const filters = input ? input.and : []; - - const embeddableInput = decode(args.config) as EmbeddableInput; - - return { - type: EmbeddableExpressionType, - input: { - ...baseEmbeddableInput, - ...embeddableInput, - filters: getQueryFilters(filters), - }, - generatedAt: Date.now(), - embeddableType: args.type, - }; - }, - - extract(state) { - const input = decode(state.config[0] as string); - - // extracts references for by-reference embeddables - if (input.savedObjectId) { - const refName = 'embeddable.savedObjectId'; - - const references: SavedObjectReference[] = [ - { - name: refName, - type: state.type[0] as string, - id: input.savedObjectId as string, - }, - ]; - - return { - state, - references, - }; - } - - // extracts references for by-value embeddables - const { state: extractedState, references: extractedReferences } = - embeddablePersistableStateService.extract({ - ...input, - type: state.type[0], - }); - - const { type, ...extractedInput } = extractedState; - - return { - state: { ...state, config: [encode(extractedInput)], type: [type] }, - references: extractedReferences, - }; - }, - - inject(state, references) { - const input = decode(state.config[0] as string); - const savedObjectReference = references.find( - (ref) => ref.name === 'embeddable.savedObjectId' - ); - - // injects saved object id for by-references embeddable - if (savedObjectReference) { - input.savedObjectId = savedObjectReference.id; - state.config[0] = encode(input); - state.type[0] = savedObjectReference.type; - } else { - // injects references for by-value embeddables - const { type, ...injectedInput } = embeddablePersistableStateService.inject( - { ...input, type: state.type[0] }, - references - ); - state.config[0] = encode(injectedInput); - state.type[0] = type; - } - return state; - }, - }; - }; -} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts index 1d69e181b5fd9d..407a0e2ebfe05a 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts @@ -5,26 +5,9 @@ * 2.0. */ -import { EmbeddableStart } from 'src/plugins/embeddable/public'; -import { embeddableFunctionFactory } from './embeddable'; import { savedLens } from './saved_lens'; import { savedMap } from './saved_map'; import { savedSearch } from './saved_search'; import { savedVisualization } from './saved_visualization'; -export interface InitializeArguments { - embeddablePersistableStateService: { - extract: EmbeddableStart['extract']; - inject: EmbeddableStart['inject']; - }; -} - -export function initFunctions(initialize: InitializeArguments) { - return [ - embeddableFunctionFactory(initialize), - savedLens, - savedMap, - savedSearch, - savedVisualization, - ]; -} +export const functions = [savedLens, savedMap, savedVisualization, savedSearch]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts index 67947691f7757c..082a69a874cae2 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts @@ -9,8 +9,9 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { PaletteOutput } from 'src/plugins/charts/common'; import { Filter as DataFilter } from '@kbn/es-query'; import { TimeRange } from 'src/plugins/data/common'; +import { EmbeddableInput } from 'src/plugins/embeddable/common'; import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; -import { ExpressionValueFilter, EmbeddableInput, TimeRange as TimeRangeArg } from '../../../types'; +import { ExpressionValueFilter, TimeRange as TimeRangeArg } from '../../../types'; import { EmbeddableTypes, EmbeddableExpressionType, @@ -26,7 +27,7 @@ interface Arguments { } export type SavedLensInput = EmbeddableInput & { - savedObjectId: string; + id: string; timeRange?: TimeRange; filters: DataFilter[]; palette?: PaletteOutput; @@ -72,19 +73,18 @@ export function savedLens(): ExpressionFunctionDefinition< }, }, type: EmbeddableExpressionType, - fn: (input, { id, timerange, title, palette }) => { + fn: (input, args) => { const filters = input ? input.and : []; return { type: EmbeddableExpressionType, input: { - id, - savedObjectId: id, + id: args.id, filters: getQueryFilters(filters), - timeRange: timerange || defaultTimeRange, - title: title === null ? undefined : title, + timeRange: args.timerange || defaultTimeRange, + title: args.title === null ? undefined : args.title, disableTriggers: true, - palette, + palette: args.palette, }, embeddableType: EmbeddableTypes.lens, generatedAt: Date.now(), diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts index a7471c755155c8..538ed3f9198239 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts @@ -30,7 +30,7 @@ const defaultTimeRange = { to: 'now', }; -type Output = EmbeddableExpression; +type Output = EmbeddableExpression; export function savedMap(): ExpressionFunctionDefinition< 'savedMap', @@ -85,9 +85,8 @@ export function savedMap(): ExpressionFunctionDefinition< return { type: EmbeddableExpressionType, input: { - id: args.id, attributes: { title: '' }, - savedObjectId: args.id, + id: args.id, filters: getQueryFilters(filters), timeRange: args.timerange || defaultTimeRange, refreshConfig: { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts index 31e3fb2a8c5643..5c0442b43250c8 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts @@ -25,7 +25,7 @@ interface Arguments { title: string | null; } -type Output = EmbeddableExpression; +type Output = EmbeddableExpression; const defaultTimeRange = { from: 'now-15m', @@ -94,7 +94,6 @@ export function savedVisualization(): ExpressionFunctionDefinition< type: EmbeddableExpressionType, input: { id, - savedObjectId: id, disableTriggers: true, timeRange: timerange || defaultTimeRange, filters: getQueryFilters(filters), diff --git a/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts b/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts index 591795637aebea..91c573fc4148ba 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts @@ -7,14 +7,12 @@ import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { ChartsPluginStart } from 'src/plugins/charts/public'; -import { PresentationUtilPluginStart } from 'src/plugins/presentation_util/public'; import { CanvasSetup } from '../public'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; import { Start as InspectorStart } from '../../../../src/plugins/inspector/public'; import { functions } from './functions/browser'; -import { initFunctions } from './functions/external'; import { typeFunctions } from './expression_types'; import { renderFunctions, renderFunctionFactories } from './renderers'; @@ -27,7 +25,6 @@ export interface StartDeps { uiActions: UiActionsStart; inspector: InspectorStart; charts: ChartsPluginStart; - presentationUtil: PresentationUtilPluginStart; } export type SetupInitializer = (core: CoreSetup, plugins: SetupDeps) => T; @@ -42,13 +39,6 @@ export class CanvasSrcPlugin implements Plugin plugins.canvas.addRenderers(renderFunctions); core.getStartServices().then(([coreStart, depsStart]) => { - const externalFunctions = initFunctions({ - embeddablePersistableStateService: { - extract: depsStart.embeddable.extract, - inject: depsStart.embeddable.inject, - }, - }); - plugins.canvas.addFunctions(externalFunctions); plugins.canvas.addRenderers( renderFunctionFactories.map((factory: any) => factory(coreStart, depsStart)) ); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx index 953746c2808406..73e839433c25e0 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx @@ -13,17 +13,16 @@ import { IEmbeddable, EmbeddableFactory, EmbeddableFactoryNotFoundError, - isErrorEmbeddable, } from '../../../../../../src/plugins/embeddable/public'; import { EmbeddableExpression } from '../../expression_types/embeddable'; import { RendererStrings } from '../../../i18n'; import { embeddableInputToExpression } from './embeddable_input_to_expression'; -import { RendererFactory, EmbeddableInput } from '../../../types'; +import { EmbeddableInput } from '../../expression_types'; +import { RendererFactory } from '../../../types'; import { CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib'; const { embeddable: strings } = RendererStrings; -// registry of references to embeddables on the workpad const embeddablesRegistry: { [key: string]: IEmbeddable | Promise; } = {}; @@ -31,11 +30,11 @@ const embeddablesRegistry: { const renderEmbeddableFactory = (core: CoreStart, plugins: StartDeps) => { const I18nContext = core.i18n.Context; - return (embeddableObject: IEmbeddable) => { + return (embeddableObject: IEmbeddable, domNode: HTMLElement) => { return (
@@ -57,9 +56,6 @@ export const embeddableRendererFactory = ( reuseDomNode: true, render: async (domNode, { input, embeddableType }, handlers) => { const uniqueId = handlers.getElementId(); - const isByValueEnabled = plugins.presentationUtil.labsService.isProjectEnabled( - 'labs:canvas:byValueEmbeddable' - ); if (!embeddablesRegistry[uniqueId]) { const factory = Array.from(plugins.embeddable.getEmbeddableFactories()).find( @@ -71,27 +67,15 @@ export const embeddableRendererFactory = ( throw new EmbeddableFactoryNotFoundError(embeddableType); } - const embeddableInput = { ...input, id: uniqueId }; - - const embeddablePromise = input.savedObjectId - ? factory - .createFromSavedObject(input.savedObjectId, embeddableInput) - .then((embeddable) => { - // stores embeddable in registrey - embeddablesRegistry[uniqueId] = embeddable; - return embeddable; - }) - : factory.create(embeddableInput).then((embeddable) => { - if (!embeddable || isErrorEmbeddable(embeddable)) { - return; - } - // stores embeddable in registry - embeddablesRegistry[uniqueId] = embeddable as IEmbeddable; - return embeddable; - }); - embeddablesRegistry[uniqueId] = embeddablePromise as Promise; - - const embeddableObject = (await (async () => embeddablePromise)()) as IEmbeddable; + const embeddablePromise = factory + .createFromSavedObject(input.id, input) + .then((embeddable) => { + embeddablesRegistry[uniqueId] = embeddable; + return embeddable; + }); + embeddablesRegistry[uniqueId] = embeddablePromise; + + const embeddableObject = await (async () => embeddablePromise)(); const palettes = await plugins.charts.palettes.getPalettes(); @@ -102,8 +86,7 @@ export const embeddableRendererFactory = ( const updatedExpression = embeddableInputToExpression( updatedInput, embeddableType, - palettes, - isByValueEnabled + palettes ); if (updatedExpression) { @@ -111,7 +94,15 @@ export const embeddableRendererFactory = ( } }); - ReactDOM.render(renderEmbeddable(embeddableObject), domNode, () => handlers.done()); + ReactDOM.render(renderEmbeddable(embeddableObject, domNode), domNode, () => + handlers.done() + ); + + handlers.onResize(() => { + ReactDOM.render(renderEmbeddable(embeddableObject, domNode), domNode, () => + handlers.done() + ); + }); handlers.onDestroy(() => { subscription.unsubscribe(); @@ -124,7 +115,6 @@ export const embeddableRendererFactory = ( } else { const embeddable = embeddablesRegistry[uniqueId]; - // updating embeddable input with changes made to expression or filters if ('updateInput' in embeddable) { embeddable.updateInput(input); embeddable.reload(); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts index 80830eac240212..41cefad6a470fa 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts @@ -10,7 +10,6 @@ import { EmbeddableTypes, EmbeddableInput } from '../../expression_types'; import { toExpression as mapToExpression } from './input_type_to_expression/map'; import { toExpression as visualizationToExpression } from './input_type_to_expression/visualization'; import { toExpression as lensToExpression } from './input_type_to_expression/lens'; -import { toExpression as genericToExpression } from './input_type_to_expression/embeddable'; export const inputToExpressionTypeMap = { [EmbeddableTypes.map]: mapToExpression, @@ -24,13 +23,8 @@ export const inputToExpressionTypeMap = { export function embeddableInputToExpression( input: EmbeddableInput, embeddableType: string, - palettes: PaletteRegistry, - useGenericEmbeddable?: boolean + palettes: PaletteRegistry ): string | undefined { - if (useGenericEmbeddable) { - return genericToExpression(input, embeddableType); - } - if (inputToExpressionTypeMap[embeddableType]) { return inputToExpressionTypeMap[embeddableType](input as any, palettes); } diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.test.ts deleted file mode 100644 index 4b78acec8750ac..00000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { toExpression } from './embeddable'; -import { EmbeddableInput } from '../../../../types'; -import { decode } from '../../../../common/lib/embeddable_dataurl'; -import { fromExpression } from '@kbn/interpreter/common'; - -describe('toExpression', () => { - describe('by-reference embeddable input', () => { - const baseEmbeddableInput = { - id: 'elementId', - savedObjectId: 'embeddableId', - filters: [], - }; - - it('converts to an embeddable expression', () => { - const input: EmbeddableInput = baseEmbeddableInput; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - expect(ast.type).toBe('expression'); - expect(ast.chain[0].function).toBe('embeddable'); - expect(ast.chain[0].arguments.type[0]).toBe('visualization'); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config.savedObjectId).toStrictEqual(input.savedObjectId); - }); - - it('includes optional input values', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: 'title', - timeRange: { - from: 'now-1h', - to: 'now', - }, - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - expect(config).toHaveProperty('timeRange'); - expect(config.timeRange).toHaveProperty('from', input.timeRange?.from); - expect(config.timeRange).toHaveProperty('to', input.timeRange?.to); - }); - - it('includes empty panel title', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: '', - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - }); - }); - - describe('by-value embeddable input', () => { - const baseEmbeddableInput = { - id: 'elementId', - disableTriggers: true, - filters: [], - }; - it('converts to an embeddable expression', () => { - const input: EmbeddableInput = baseEmbeddableInput; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - expect(ast.type).toBe('expression'); - expect(ast.chain[0].function).toBe('embeddable'); - expect(ast.chain[0].arguments.type[0]).toBe('visualization'); - - const config = decode(ast.chain[0].arguments.config[0] as string); - expect(config.filters).toStrictEqual(input.filters); - expect(config.disableTriggers).toStrictEqual(input.disableTriggers); - }); - - it('includes optional input values', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: 'title', - timeRange: { - from: 'now-1h', - to: 'now', - }, - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - expect(config).toHaveProperty('timeRange'); - expect(config.timeRange).toHaveProperty('from', input.timeRange?.from); - expect(config.timeRange).toHaveProperty('to', input.timeRange?.to); - }); - - it('includes empty panel title', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: '', - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - }); - }); -}); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts deleted file mode 100644 index 94d86f6640be1d..00000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { encode } from '../../../../common/lib/embeddable_dataurl'; -import { EmbeddableInput } from '../../../expression_types'; - -export function toExpression(input: EmbeddableInput, embeddableType: string): string { - return `embeddable config="${encode(input)}" type="${embeddableType}"`; -} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts index 224cdfba389d77..24da7238bcee94 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts @@ -11,8 +11,7 @@ import { fromExpression, Ast } from '@kbn/interpreter/common'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; const baseEmbeddableInput = { - id: 'elementId', - savedObjectId: 'embeddableId', + id: 'embeddableId', filters: [], }; @@ -28,7 +27,7 @@ describe('toExpression', () => { expect(ast.type).toBe('expression'); expect(ast.chain[0].function).toBe('savedLens'); - expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]); + expect(ast.chain[0].arguments.id).toStrictEqual([input.id]); expect(ast.chain[0].arguments).not.toHaveProperty('title'); expect(ast.chain[0].arguments).not.toHaveProperty('timerange'); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts index 5a13b73b3fe746..35e106f234fa4e 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts @@ -14,7 +14,7 @@ export function toExpression(input: SavedLensInput, palettes: PaletteRegistry): expressionParts.push('savedLens'); - expressionParts.push(`id="${input.savedObjectId}"`); + expressionParts.push(`id="${input.id}"`); if (input.title !== undefined) { expressionParts.push(`title="${input.title}"`); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts index af7b40a9b283d9..804d0d849cc7f7 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts @@ -6,12 +6,12 @@ */ import { toExpression } from './map'; +import { MapEmbeddableInput } from '../../../../../../plugins/maps/public/embeddable'; import { fromExpression, Ast } from '@kbn/interpreter/common'; const baseSavedMapInput = { - id: 'elementId', attributes: { title: '' }, - savedObjectId: 'embeddableId', + id: 'embeddableId', filters: [], isLayerTOCOpen: false, refreshConfig: { @@ -23,7 +23,7 @@ const baseSavedMapInput = { describe('toExpression', () => { it('converts to a savedMap expression', () => { - const input = { + const input: MapEmbeddableInput = { ...baseSavedMapInput, }; @@ -33,7 +33,7 @@ describe('toExpression', () => { expect(ast.type).toBe('expression'); expect(ast.chain[0].function).toBe('savedMap'); - expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]); + expect(ast.chain[0].arguments.id).toStrictEqual([input.id]); expect(ast.chain[0].arguments).not.toHaveProperty('title'); expect(ast.chain[0].arguments).not.toHaveProperty('center'); @@ -41,7 +41,7 @@ describe('toExpression', () => { }); it('includes optional input values', () => { - const input = { + const input: MapEmbeddableInput = { ...baseSavedMapInput, mapCenter: { lat: 1, @@ -73,7 +73,7 @@ describe('toExpression', () => { }); it('includes empty panel title', () => { - const input = { + const input: MapEmbeddableInput = { ...baseSavedMapInput, title: '', }; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts index 03746f38b4696c..3fd6a68a327c60 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts @@ -5,14 +5,13 @@ * 2.0. */ -import { MapEmbeddableInput } from '../../../../../../plugins/maps/public'; +import { MapEmbeddableInput } from '../../../../../../plugins/maps/public/embeddable'; -export function toExpression(input: MapEmbeddableInput & { savedObjectId: string }): string { +export function toExpression(input: MapEmbeddableInput): string { const expressionParts = [] as string[]; expressionParts.push('savedMap'); - - expressionParts.push(`id="${input.savedObjectId}"`); + expressionParts.push(`id="${input.id}"`); if (input.title !== undefined) { expressionParts.push(`title="${input.title}"`); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts index 4c61a130f3c95f..c5106b9a102b40 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts @@ -9,8 +9,7 @@ import { toExpression } from './visualization'; import { fromExpression, Ast } from '@kbn/interpreter/common'; const baseInput = { - id: 'elementId', - savedObjectId: 'embeddableId', + id: 'embeddableId', }; describe('toExpression', () => { @@ -25,7 +24,7 @@ describe('toExpression', () => { expect(ast.type).toBe('expression'); expect(ast.chain[0].function).toBe('savedVisualization'); - expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]); + expect(ast.chain[0].arguments.id).toStrictEqual([input.id]); }); it('includes timerange if given', () => { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts index 364d7cd0755db4..bcb73b2081fee5 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts @@ -7,11 +7,11 @@ import { VisualizeInput } from 'src/plugins/visualizations/public'; -export function toExpression(input: VisualizeInput & { savedObjectId: string }): string { +export function toExpression(input: VisualizeInput): string { const expressionParts = [] as string[]; expressionParts.push('savedVisualization'); - expressionParts.push(`id="${input.savedObjectId}"`); + expressionParts.push(`id="${input.id}"`); if (input.title !== undefined) { expressionParts.push(`title="${input.title}"`); diff --git a/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts b/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts deleted file mode 100644 index e76dedfe63b14a..00000000000000 --- a/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EmbeddableInput } from '../../types'; - -export const encode = (input: Partial) => - Buffer.from(JSON.stringify(input)).toString('base64'); -export const decode = (serializedInput: string) => - JSON.parse(Buffer.from(serializedInput, 'base64').toString()); diff --git a/x-pack/plugins/canvas/i18n/functions/dict/embeddable.ts b/x-pack/plugins/canvas/i18n/functions/dict/embeddable.ts deleted file mode 100644 index 279f58799e8c00..00000000000000 --- a/x-pack/plugins/canvas/i18n/functions/dict/embeddable.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { embeddableFunctionFactory } from '../../../canvas_plugin_src/functions/external/embeddable'; -import { FunctionHelp } from '../function_help'; -import { FunctionFactory } from '../../../types'; - -export const help: FunctionHelp>> = { - help: i18n.translate('xpack.canvas.functions.embeddableHelpText', { - defaultMessage: `Returns an embeddable with the provided configuration`, - }), - args: { - config: i18n.translate('xpack.canvas.functions.embeddable.args.idHelpText', { - defaultMessage: `The base64 encoded embeddable input object`, - }), - type: i18n.translate('xpack.canvas.functions.embeddable.args.typeHelpText', { - defaultMessage: `The embeddable type`, - }), - }, -}; diff --git a/x-pack/plugins/canvas/i18n/functions/function_help.ts b/x-pack/plugins/canvas/i18n/functions/function_help.ts index 520d32af1c272c..5eae785fefa2ea 100644 --- a/x-pack/plugins/canvas/i18n/functions/function_help.ts +++ b/x-pack/plugins/canvas/i18n/functions/function_help.ts @@ -27,7 +27,6 @@ import { help as demodata } from './dict/demodata'; import { help as doFn } from './dict/do'; import { help as dropdownControl } from './dict/dropdown_control'; import { help as eq } from './dict/eq'; -import { help as embeddable } from './dict/embeddable'; import { help as escount } from './dict/escount'; import { help as esdocs } from './dict/esdocs'; import { help as essql } from './dict/essql'; @@ -183,7 +182,6 @@ export const getFunctionHelp = (): FunctionHelpDict => ({ do: doFn, dropdownControl, eq, - embeddable, escount, esdocs, essql, diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index 2fd312502a3c74..9c4d1b2179d821 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -25,7 +25,6 @@ "features", "inspector", "presentationUtil", - "visualizations", "uiActions", "share" ], diff --git a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx index 57f52fcf21f0f9..bf731876bf8c88 100644 --- a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx +++ b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC, useCallback } from 'react'; +import React, { FC } from 'react'; import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -27,44 +27,38 @@ const strings = { }; export interface Props { onClose: () => void; - onSelect: (id: string, embeddableType: string, isByValueEnabled?: boolean) => void; + onSelect: (id: string, embeddableType: string) => void; availableEmbeddables: string[]; - isByValueEnabled?: boolean; } -export const AddEmbeddableFlyout: FC = ({ - onSelect, - availableEmbeddables, - onClose, - isByValueEnabled, -}) => { +export const AddEmbeddableFlyout: FC = ({ onSelect, availableEmbeddables, onClose }) => { const embeddablesService = useEmbeddablesService(); const platformService = usePlatformService(); const { getEmbeddableFactories } = embeddablesService; const { getSavedObjects, getUISettings } = platformService; - const onAddPanel = useCallback( - (id: string, savedObjectType: string) => { - const embeddableFactories = getEmbeddableFactories(); - // Find the embeddable type from the saved object type - const found = Array.from(embeddableFactories).find((embeddableFactory) => { - return Boolean( - embeddableFactory.savedObjectMetaData && - embeddableFactory.savedObjectMetaData.type === savedObjectType - ); - }); + const onAddPanel = (id: string, savedObjectType: string, name: string) => { + const embeddableFactories = getEmbeddableFactories(); - const foundEmbeddableType = found ? found.type : 'unknown'; + // Find the embeddable type from the saved object type + const found = Array.from(embeddableFactories).find((embeddableFactory) => { + return Boolean( + embeddableFactory.savedObjectMetaData && + embeddableFactory.savedObjectMetaData.type === savedObjectType + ); + }); - onSelect(id, foundEmbeddableType, isByValueEnabled); - }, - [isByValueEnabled, getEmbeddableFactories, onSelect] - ); + const foundEmbeddableType = found ? found.type : 'unknown'; + + onSelect(id, foundEmbeddableType); + }; const embeddableFactories = getEmbeddableFactories(); const availableSavedObjects = Array.from(embeddableFactories) - .filter((factory) => isByValueEnabled || availableEmbeddables.includes(factory.type)) + .filter((factory) => { + return availableEmbeddables.includes(factory.type); + }) .map((factory) => factory.savedObjectMetaData) .filter>(function ( maybeSavedObjectMetaData diff --git a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx index 4dc8d963932d8f..770a4cac606b0b 100644 --- a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx +++ b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx @@ -8,14 +8,12 @@ import React, { useMemo, useEffect, useCallback } from 'react'; import { createPortal } from 'react-dom'; import { useSelector, useDispatch } from 'react-redux'; -import { encode } from '../../../common/lib/embeddable_dataurl'; import { AddEmbeddableFlyout as Component, Props as ComponentProps } from './flyout.component'; // @ts-expect-error untyped local import { addElement } from '../../state/actions/elements'; import { getSelectedPage } from '../../state/selectors/workpad'; import { EmbeddableTypes } from '../../../canvas_plugin_src/expression_types/embeddable'; import { State } from '../../../types'; -import { useLabsService } from '../../services'; const allowedEmbeddables = { [EmbeddableTypes.map]: (id: string) => { @@ -67,9 +65,6 @@ export const AddEmbeddablePanel: React.FunctionComponent = ({ availableEmbeddables, ...restProps }) => { - const labsService = useLabsService(); - const isByValueEnabled = labsService.isProjectEnabled('labs:canvas:byValueEmbeddable'); - const dispatch = useDispatch(); const pageId = useSelector((state) => getSelectedPage(state)); @@ -79,27 +74,18 @@ export const AddEmbeddablePanel: React.FunctionComponent = ({ ); const onSelect = useCallback( - (id: string, type: string): void => { + (id: string, type: string) => { const partialElement = { expression: `markdown "Could not find embeddable for type ${type}" | render`, }; - - // If by-value is enabled, we'll handle both by-reference and by-value embeddables - // with the new generic `embeddable` function. - // Otherwise we fallback to the embeddable type specific expressions. - if (isByValueEnabled) { - const config = encode({ savedObjectId: id }); - partialElement.expression = `embeddable config="${config}" - type="${type}" -| render`; - } else if (allowedEmbeddables[type]) { + if (allowedEmbeddables[type]) { partialElement.expression = allowedEmbeddables[type](id); } addEmbeddable(pageId, partialElement); restProps.onClose(); }, - [addEmbeddable, pageId, restProps, isByValueEnabled] + [addEmbeddable, pageId, restProps] ); return ( @@ -107,7 +93,6 @@ export const AddEmbeddablePanel: React.FunctionComponent = ({ {...restProps} availableEmbeddables={availableEmbeddables || []} onSelect={onSelect} - isByValueEnabled={isByValueEnabled} /> ); }; diff --git a/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx b/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx index ffd5b095b12e52..50d527036560ad 100644 --- a/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx +++ b/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx @@ -6,5 +6,3 @@ */ export { useDownloadWorkpad, useDownloadRenderedWorkpad } from './use_download_workpad'; - -export { useIncomingEmbeddable } from './use_incoming_embeddable'; diff --git a/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts b/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts deleted file mode 100644 index 2f8e2503ea57ef..00000000000000 --- a/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; -import { fromExpression } from '@kbn/interpreter/common'; -import { CANVAS_APP } from '../../../../common/lib'; -import { decode, encode } from '../../../../common/lib/embeddable_dataurl'; -import { CanvasElement, CanvasPage } from '../../../../types'; -import { useEmbeddablesService, useLabsService } from '../../../services'; -// @ts-expect-error unconverted file -import { addElement } from '../../../state/actions/elements'; -// @ts-expect-error unconverted file -import { selectToplevelNodes } from '../../../state/actions/transient'; - -import { - updateEmbeddableExpression, - fetchEmbeddableRenderable, -} from '../../../state/actions/embeddable'; -import { clearValue } from '../../../state/actions/resolved_args'; - -export const useIncomingEmbeddable = (selectedPage: CanvasPage) => { - const embeddablesService = useEmbeddablesService(); - const labsService = useLabsService(); - const dispatch = useDispatch(); - const isByValueEnabled = labsService.isProjectEnabled('labs:canvas:byValueEmbeddable'); - const stateTransferService = embeddablesService.getStateTransfer(); - - // fetch incoming embeddable from state transfer service. - const incomingEmbeddable = stateTransferService.getIncomingEmbeddablePackage(CANVAS_APP, true); - - useEffect(() => { - if (isByValueEnabled && incomingEmbeddable) { - const { embeddableId, input: incomingInput, type } = incomingEmbeddable; - - // retrieve existing element - const originalElement = selectedPage.elements.find( - ({ id }: CanvasElement) => id === embeddableId - ); - - if (originalElement) { - const originalAst = fromExpression(originalElement!.expression); - - const functionIndex = originalAst.chain.findIndex( - ({ function: fn }) => fn === 'embeddable' - ); - - const originalInput = decode( - originalAst.chain[functionIndex].arguments.config[0] as string - ); - - // clear out resolved arg for old embeddable - const argumentPath = [embeddableId, 'expressionRenderable']; - dispatch(clearValue({ path: argumentPath })); - - const updatedInput = { ...originalInput, ...incomingInput }; - - const expression = `embeddable config="${encode(updatedInput)}" - type="${type}" -| render`; - - dispatch( - updateEmbeddableExpression({ - elementId: originalElement.id, - embeddableExpression: expression, - }) - ); - - // update resolved args - dispatch(fetchEmbeddableRenderable(originalElement.id)); - - // select new embeddable element - dispatch(selectToplevelNodes([embeddableId])); - } else { - const expression = `embeddable config="${encode(incomingInput)}" - type="${type}" -| render`; - dispatch(addElement(selectedPage.id, { expression })); - } - } - }, [dispatch, selectedPage, incomingEmbeddable, isByValueEnabled]); -}; diff --git a/x-pack/plugins/canvas/public/components/workpad/workpad.tsx b/x-pack/plugins/canvas/public/components/workpad/workpad.tsx index 7cc077203c7372..622c885b6ef281 100644 --- a/x-pack/plugins/canvas/public/components/workpad/workpad.tsx +++ b/x-pack/plugins/canvas/public/components/workpad/workpad.tsx @@ -27,7 +27,6 @@ import { WorkpadRoutingContext } from '../../routes/workpad'; import { usePlatformService } from '../../services'; import { Workpad as WorkpadComponent, Props } from './workpad.component'; import { State } from '../../../types'; -import { useIncomingEmbeddable } from '../hooks'; type ContainerProps = Pick; @@ -59,9 +58,6 @@ export const Workpad: FC = (props) => { }; }); - const selectedPage = propsFromState.pages[propsFromState.selectedPageNumber - 1]; - useIncomingEmbeddable(selectedPage); - const fetchAllRenderables = useCallback(() => { dispatch(fetchAllRenderablesAction()); }, [dispatch]); diff --git a/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss b/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss index 0ddd44ed8f9a81..4acdca10d61cc2 100644 --- a/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss +++ b/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss @@ -31,7 +31,7 @@ $canvasLayoutFontSize: $euiFontSizeS; .canvasLayout__stageHeader { flex-grow: 0; flex-basis: auto; - padding: $euiSizeS $euiSize; + padding: $euiSizeS; font-size: $canvasLayoutFontSize; border-bottom: $euiBorderThin; background: $euiColorLightestShade; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot deleted file mode 100644 index f4aab0e59e7ee6..00000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot +++ /dev/null @@ -1,81 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Storyshots components/WorkpadHeader/EditorMenu dark mode 1`] = ` -
-
- -
-
-`; - -exports[`Storyshots components/WorkpadHeader/EditorMenu default 1`] = ` -
-
- -
-
-`; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx deleted file mode 100644 index 01048bc0af3010..00000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { storiesOf } from '@storybook/react'; -import { action } from '@storybook/addon-actions'; -import React from 'react'; -import { EmbeddableFactoryDefinition, IEmbeddable } from 'src/plugins/embeddable/public'; -import { BaseVisType, VisTypeAlias } from 'src/plugins/visualizations/public'; -import { EditorMenu } from '../editor_menu.component'; - -const testFactories: EmbeddableFactoryDefinition[] = [ - { - type: 'ml_anomaly_swimlane', - getDisplayName: () => 'Anomaly swimlane', - getIconType: () => '', - getDescription: () => 'Description for anomaly swimlane', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'swimlane_embeddable' } as IEmbeddable), - grouping: [ - { - id: 'ml', - getDisplayName: () => 'machine learning', - getIconType: () => 'machineLearningApp', - }, - ], - }, - { - type: 'ml_anomaly_chart', - getDisplayName: () => 'Anomaly chart', - getIconType: () => '', - getDescription: () => 'Description for anomaly chart', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'anomaly_chart_embeddable' } as IEmbeddable), - grouping: [ - { - id: 'ml', - getDisplayName: () => 'machine learning', - getIconType: () => 'machineLearningApp', - }, - ], - }, - { - type: 'log_stream', - getDisplayName: () => 'Log stream', - getIconType: () => '', - getDescription: () => 'Description for log stream', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'anomaly_chart_embeddable' } as IEmbeddable), - }, -]; - -const testVisTypes: BaseVisType[] = [ - { title: 'TSVB', icon: '', description: 'Description of TSVB', name: 'tsvb' } as BaseVisType, - { - titleInWizard: 'Custom visualization', - title: 'Vega', - icon: '', - description: 'Description of Vega', - name: 'vega', - } as BaseVisType, -]; - -const testVisTypeAliases: VisTypeAlias[] = [ - { - title: 'Lens', - aliasApp: 'lens', - aliasPath: 'path/to/lens', - icon: 'lensApp', - name: 'lens', - description: 'Description of Lens app', - stage: 'production', - }, - { - title: 'Maps', - aliasApp: 'maps', - aliasPath: 'path/to/maps', - icon: 'gisApp', - name: 'maps', - description: 'Description of Maps app', - stage: 'production', - }, -]; - -storiesOf('components/WorkpadHeader/EditorMenu', module) - .add('default', () => ( - action('createNewVisType')} - createNewEmbeddable={() => action('createNewEmbeddable')} - /> - )) - .add('dark mode', () => ( - action('createNewVisType')} - createNewEmbeddable={() => action('createNewEmbeddable')} - /> - )); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx deleted file mode 100644 index e8f762f9731a19..00000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FC } from 'react'; -import { - EuiContextMenu, - EuiContextMenuPanelItemDescriptor, - EuiContextMenuItemIcon, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { EmbeddableFactoryDefinition } from '../../../../../../../src/plugins/embeddable/public'; -import { BaseVisType, VisTypeAlias } from '../../../../../../../src/plugins/visualizations/public'; -import { SolutionToolbarPopover } from '../../../../../../../src/plugins/presentation_util/public'; - -const strings = { - getEditorMenuButtonLabel: () => - i18n.translate('xpack.canvas.solutionToolbar.editorMenuButtonLabel', { - defaultMessage: 'Select type', - }), -}; - -interface FactoryGroup { - id: string; - appName: string; - icon: EuiContextMenuItemIcon; - panelId: number; - factories: EmbeddableFactoryDefinition[]; -} - -interface Props { - factories: EmbeddableFactoryDefinition[]; - isDarkThemeEnabled?: boolean; - promotedVisTypes: BaseVisType[]; - visTypeAliases: VisTypeAlias[]; - createNewVisType: (visType?: BaseVisType | VisTypeAlias) => () => void; - createNewEmbeddable: (factory: EmbeddableFactoryDefinition) => () => void; -} - -export const EditorMenu: FC = ({ - factories, - isDarkThemeEnabled, - promotedVisTypes, - visTypeAliases, - createNewVisType, - createNewEmbeddable, -}: Props) => { - const factoryGroupMap: Record = {}; - const ungroupedFactories: EmbeddableFactoryDefinition[] = []; - - let panelCount = 1; - - // Maps factories with a group to create nested context menus for each group type - // and pushes ungrouped factories into a separate array - factories.forEach((factory: EmbeddableFactoryDefinition, index) => { - const { grouping } = factory; - - if (grouping) { - grouping.forEach((group) => { - if (factoryGroupMap[group.id]) { - factoryGroupMap[group.id].factories.push(factory); - } else { - factoryGroupMap[group.id] = { - id: group.id, - appName: group.getDisplayName ? group.getDisplayName({}) : group.id, - icon: (group.getIconType ? group.getIconType({}) : 'empty') as EuiContextMenuItemIcon, - factories: [factory], - panelId: panelCount, - }; - - panelCount++; - } - }); - } else { - ungroupedFactories.push(factory); - } - }); - - const getVisTypeMenuItem = (visType: BaseVisType): EuiContextMenuPanelItemDescriptor => { - const { name, title, titleInWizard, description, icon = 'empty' } = visType; - return { - name: titleInWizard || title, - icon: icon as string, - onClick: createNewVisType(visType), - 'data-test-subj': `visType-${name}`, - toolTipContent: description, - }; - }; - - const getVisTypeAliasMenuItem = ( - visTypeAlias: VisTypeAlias - ): EuiContextMenuPanelItemDescriptor => { - const { name, title, description, icon = 'empty' } = visTypeAlias; - - return { - name: title, - icon, - onClick: createNewVisType(visTypeAlias), - 'data-test-subj': `visType-${name}`, - toolTipContent: description, - }; - }; - - const getEmbeddableFactoryMenuItem = ( - factory: EmbeddableFactoryDefinition - ): EuiContextMenuPanelItemDescriptor => { - const icon = factory?.getIconType ? factory.getIconType() : 'empty'; - - const toolTipContent = factory?.getDescription ? factory.getDescription() : undefined; - - return { - name: factory.getDisplayName(), - icon, - toolTipContent, - onClick: createNewEmbeddable(factory), - 'data-test-subj': `createNew-${factory.type}`, - }; - }; - - const editorMenuPanels = [ - { - id: 0, - items: [ - ...visTypeAliases.map(getVisTypeAliasMenuItem), - ...Object.values(factoryGroupMap).map(({ id, appName, icon, panelId }) => ({ - name: appName, - icon, - panel: panelId, - 'data-test-subj': `canvasEditorMenu-${id}Group`, - })), - ...ungroupedFactories.map(getEmbeddableFactoryMenuItem), - ...promotedVisTypes.map(getVisTypeMenuItem), - ], - }, - ...Object.values(factoryGroupMap).map( - ({ appName, panelId, factories: groupFactories }: FactoryGroup) => ({ - id: panelId, - title: appName, - items: groupFactories.map(getEmbeddableFactoryMenuItem), - }) - ), - ]; - - return ( - - {() => ( - - )} - - ); -}; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx deleted file mode 100644 index dad34e6983c5db..00000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FC, useCallback } from 'react'; -import { useLocation } from 'react-router-dom'; -import { trackCanvasUiMetric, METRIC_TYPE } from '../../../../public/lib/ui_metric'; -import { - useEmbeddablesService, - usePlatformService, - useVisualizationsService, -} from '../../../services'; -import { - BaseVisType, - VisGroups, - VisTypeAlias, -} from '../../../../../../../src/plugins/visualizations/public'; -import { - EmbeddableFactoryDefinition, - EmbeddableInput, -} from '../../../../../../../src/plugins/embeddable/public'; -import { CANVAS_APP } from '../../../../common/lib'; -import { encode } from '../../../../common/lib/embeddable_dataurl'; -import { ElementSpec } from '../../../../types'; -import { EditorMenu as Component } from './editor_menu.component'; - -interface Props { - /** - * Handler for adding a selected element to the workpad - */ - addElement: (element: Partial) => void; -} - -export const EditorMenu: FC = ({ addElement }) => { - const embeddablesService = useEmbeddablesService(); - const { pathname, search } = useLocation(); - const platformService = usePlatformService(); - const stateTransferService = embeddablesService.getStateTransfer(); - const visualizationsService = useVisualizationsService(); - const IS_DARK_THEME = platformService.getUISetting('theme:darkMode'); - - const createNewVisType = useCallback( - (visType?: BaseVisType | VisTypeAlias) => () => { - let path = ''; - let appId = ''; - - if (visType) { - if (trackCanvasUiMetric) { - trackCanvasUiMetric(METRIC_TYPE.CLICK, `${visType.name}:create`); - } - - if ('aliasPath' in visType) { - appId = visType.aliasApp; - path = visType.aliasPath; - } else { - appId = 'visualize'; - path = `#/create?type=${encodeURIComponent(visType.name)}`; - } - } else { - appId = 'visualize'; - path = '#/create?'; - } - - stateTransferService.navigateToEditor(appId, { - path, - state: { - originatingApp: CANVAS_APP, - originatingPath: `#/${pathname}${search}`, - }, - }); - }, - [stateTransferService, pathname, search] - ); - - const createNewEmbeddable = useCallback( - (factory: EmbeddableFactoryDefinition) => async () => { - if (trackCanvasUiMetric) { - trackCanvasUiMetric(METRIC_TYPE.CLICK, factory.type); - } - let embeddableInput; - if (factory.getExplicitInput) { - embeddableInput = await factory.getExplicitInput(); - } else { - const newEmbeddable = await factory.create({} as EmbeddableInput); - embeddableInput = newEmbeddable?.getInput(); - } - - if (embeddableInput) { - const config = encode(embeddableInput); - const expression = `embeddable config="${config}" - type="${factory.type}" -| render`; - - addElement({ expression }); - } - }, - [addElement] - ); - - const getVisTypesByGroup = (group: VisGroups): BaseVisType[] => - visualizationsService - .getByGroup(group) - .sort(({ name: a }: BaseVisType | VisTypeAlias, { name: b }: BaseVisType | VisTypeAlias) => { - if (a < b) { - return -1; - } - if (a > b) { - return 1; - } - return 0; - }) - .filter(({ hidden }: BaseVisType) => !hidden); - - const visTypeAliases = visualizationsService - .getAliases() - .sort(({ promotion: a = false }: VisTypeAlias, { promotion: b = false }: VisTypeAlias) => - a === b ? 0 : a ? -1 : 1 - ); - - const factories = embeddablesService - ? Array.from(embeddablesService.getEmbeddableFactories()).filter( - ({ type, isEditable, canCreateNew, isContainerType }) => - isEditable() && - !isContainerType && - canCreateNew() && - !['visualization', 'ml'].some((factoryType) => { - return type.includes(factoryType); - }) - ) - : []; - - const promotedVisTypes = getVisTypesByGroup(VisGroups.PROMOTED); - - return ( - - ); -}; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/index.ts b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/index.ts deleted file mode 100644 index 0f903b1bbbe2ed..00000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { EditorMenu } from './editor_menu'; -export { EditorMenu as EditorMenuComponent } from './editor_menu.component'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx index 1cfab236d9a9c9..8ac581b0866a46 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx @@ -12,11 +12,11 @@ import { EuiContextMenu, EuiIcon, EuiContextMenuPanelItemDescriptor } from '@ela import { i18n } from '@kbn/i18n'; import { PrimaryActionPopover } from '../../../../../../../src/plugins/presentation_util/public'; import { getId } from '../../../lib/get_id'; +import { ClosePopoverFn } from '../../popover'; import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib'; import { ElementSpec } from '../../../../types'; import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; import { AssetManager } from '../../asset_manager'; -import { ClosePopoverFn } from '../../popover'; import { SavedElementsModal } from '../../saved_elements_modal'; interface CategorizedElementLists { @@ -112,7 +112,7 @@ const categorizeElementsByType = (elements: ElementSpec[]): { [key: string]: Ele return categories; }; -export interface Props { +interface Props { /** * Dictionary of elements from elements registry */ @@ -120,7 +120,7 @@ export interface Props { /** * Handler for adding a selected element to the workpad */ - addElement: (element: Partial) => void; + addElement: (element: ElementSpec) => void; } export const ElementMenu: FunctionComponent = ({ elements, addElement }) => { diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts index 037bb84b0cdba4..52c8daece7690a 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts +++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts @@ -5,4 +5,5 @@ * 2.0. */ -export { ElementMenu } from './element_menu.component'; +export { ElementMenu } from './element_menu'; +export { ElementMenu as ElementMenuComponent } from './element_menu.component'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx index b84e4faf2925e7..f031d7c2631991 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx @@ -27,7 +27,6 @@ import { ElementMenu } from './element_menu'; import { ShareMenu } from './share_menu'; import { ViewMenu } from './view_menu'; import { LabsControl } from './labs_control'; -import { EditorMenu } from './editor_menu'; const strings = { getFullScreenButtonAriaLabel: () => @@ -161,22 +160,24 @@ export const WorkpadHeader: FC = ({ - {isWriteable && ( - - - {{ - primaryActionButton: , - quickButtonGroup: , - addFromLibraryButton: , - extraButtons: [], - }} - - - )} + {isWriteable && ( + + + {{ + primaryActionButton: ( + + ), + quickButtonGroup: , + addFromLibraryButton: , + }} + + + )} @@ -191,7 +192,6 @@ export const WorkpadHeader: FC = ({ - diff --git a/x-pack/plugins/canvas/public/plugin.tsx b/x-pack/plugins/canvas/public/plugin.tsx index 912055dd47a627..723d1afea2860f 100644 --- a/x-pack/plugins/canvas/public/plugin.tsx +++ b/x-pack/plugins/canvas/public/plugin.tsx @@ -8,7 +8,6 @@ import { BehaviorSubject } from 'rxjs'; import type { SharePluginSetup } from 'src/plugins/share/public'; import { ChartsPluginSetup, ChartsPluginStart } from 'src/plugins/charts/public'; -import { VisualizationsStart } from 'src/plugins/visualizations/public'; import { ReportingStart } from '../../reporting/public'; import { CoreSetup, @@ -64,7 +63,6 @@ export interface CanvasStartDeps { charts: ChartsPluginStart; data: DataPublicPluginStart; presentationUtil: PresentationUtilPluginStart; - visualizations: VisualizationsStart; spaces?: SpacesPluginStart; } @@ -124,12 +122,7 @@ export class CanvasPlugin const { pluginServices } = await import('./services'); pluginServices.setRegistry( - pluginServiceRegistry.start({ - coreStart, - startPlugins, - appUpdater: this.appUpdater, - initContext: this.initContext, - }) + pluginServiceRegistry.start({ coreStart, startPlugins, initContext: this.initContext }) ); // Load application bundle diff --git a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts index bd9a4e7141c272..35e79b442a15d7 100644 --- a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts +++ b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts @@ -53,24 +53,14 @@ export const useWorkpad = ( workpad.aliasId = aliasId; } - if (storedWorkpad.id !== workpadId || storedWorkpad.aliasId !== aliasId) { - dispatch(setAssets(assets)); - dispatch(setWorkpad(workpad, { loadPages })); - dispatch(setZoomScale(1)); - } + dispatch(setAssets(assets)); + dispatch(setWorkpad(workpad, { loadPages })); + dispatch(setZoomScale(1)); } catch (e) { setError(e as Error | string); } })(); - }, [ - workpadId, - dispatch, - setError, - loadPages, - workpadResolve, - storedWorkpad.id, - storedWorkpad.aliasId, - ]); + }, [workpadId, dispatch, setError, loadPages, workpadResolve]); useEffect(() => { (() => { diff --git a/x-pack/plugins/canvas/public/services/embeddables.ts b/x-pack/plugins/canvas/public/services/embeddables.ts index 26b150b7a53493..24d7a57e086f2d 100644 --- a/x-pack/plugins/canvas/public/services/embeddables.ts +++ b/x-pack/plugins/canvas/public/services/embeddables.ts @@ -5,12 +5,8 @@ * 2.0. */ -import { - EmbeddableFactory, - EmbeddableStateTransfer, -} from '../../../../../src/plugins/embeddable/public'; +import { EmbeddableFactory } from '../../../../../src/plugins/embeddable/public'; export interface CanvasEmbeddablesService { getEmbeddableFactories: () => IterableIterator; - getStateTransfer: () => EmbeddableStateTransfer; } diff --git a/x-pack/plugins/canvas/public/services/index.ts b/x-pack/plugins/canvas/public/services/index.ts index ed55f919e4c767..f4292810b80896 100644 --- a/x-pack/plugins/canvas/public/services/index.ts +++ b/x-pack/plugins/canvas/public/services/index.ts @@ -17,7 +17,6 @@ import { CanvasNavLinkService } from './nav_link'; import { CanvasNotifyService } from './notify'; import { CanvasPlatformService } from './platform'; import { CanvasReportingService } from './reporting'; -import { CanvasVisualizationsService } from './visualizations'; import { CanvasWorkpadService } from './workpad'; export interface CanvasPluginServices { @@ -29,7 +28,6 @@ export interface CanvasPluginServices { notify: CanvasNotifyService; platform: CanvasPlatformService; reporting: CanvasReportingService; - visualizations: CanvasVisualizationsService; workpad: CanvasWorkpadService; } @@ -46,6 +44,4 @@ export const useNavLinkService = () => (() => pluginServices.getHooks().navLink. export const useNotifyService = () => (() => pluginServices.getHooks().notify.useService())(); export const usePlatformService = () => (() => pluginServices.getHooks().platform.useService())(); export const useReportingService = () => (() => pluginServices.getHooks().reporting.useService())(); -export const useVisualizationsService = () => - (() => pluginServices.getHooks().visualizations.useService())(); export const useWorkpadService = () => (() => pluginServices.getHooks().workpad.useService())(); diff --git a/x-pack/plugins/canvas/public/services/kibana/embeddables.ts b/x-pack/plugins/canvas/public/services/kibana/embeddables.ts index 8d1a86edab3d89..054b9da7409fbb 100644 --- a/x-pack/plugins/canvas/public/services/kibana/embeddables.ts +++ b/x-pack/plugins/canvas/public/services/kibana/embeddables.ts @@ -16,5 +16,4 @@ export type EmbeddablesServiceFactory = KibanaPluginServiceFactory< export const embeddablesServiceFactory: EmbeddablesServiceFactory = ({ startPlugins }) => ({ getEmbeddableFactories: startPlugins.embeddable.getEmbeddableFactories, - getStateTransfer: startPlugins.embeddable.getStateTransfer, }); diff --git a/x-pack/plugins/canvas/public/services/kibana/index.ts b/x-pack/plugins/canvas/public/services/kibana/index.ts index 91767947bc0a65..1eb010e8d6f9da 100644 --- a/x-pack/plugins/canvas/public/services/kibana/index.ts +++ b/x-pack/plugins/canvas/public/services/kibana/index.ts @@ -22,7 +22,6 @@ import { navLinkServiceFactory } from './nav_link'; import { notifyServiceFactory } from './notify'; import { platformServiceFactory } from './platform'; import { reportingServiceFactory } from './reporting'; -import { visualizationsServiceFactory } from './visualizations'; import { workpadServiceFactory } from './workpad'; export { customElementServiceFactory } from './custom_element'; @@ -32,7 +31,6 @@ export { labsServiceFactory } from './labs'; export { notifyServiceFactory } from './notify'; export { platformServiceFactory } from './platform'; export { reportingServiceFactory } from './reporting'; -export { visualizationsServiceFactory } from './visualizations'; export { workpadServiceFactory } from './workpad'; export const pluginServiceProviders: PluginServiceProviders< @@ -47,7 +45,6 @@ export const pluginServiceProviders: PluginServiceProviders< notify: new PluginServiceProvider(notifyServiceFactory), platform: new PluginServiceProvider(platformServiceFactory), reporting: new PluginServiceProvider(reportingServiceFactory), - visualizations: new PluginServiceProvider(visualizationsServiceFactory), workpad: new PluginServiceProvider(workpadServiceFactory), }; diff --git a/x-pack/plugins/canvas/public/services/kibana/visualizations.ts b/x-pack/plugins/canvas/public/services/kibana/visualizations.ts deleted file mode 100644 index e319ec1c1f4272..00000000000000 --- a/x-pack/plugins/canvas/public/services/kibana/visualizations.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { KibanaPluginServiceFactory } from '../../../../../../src/plugins/presentation_util/public'; -import { CanvasStartDeps } from '../../plugin'; -import { CanvasVisualizationsService } from '../visualizations'; - -export type VisualizationsServiceFactory = KibanaPluginServiceFactory< - CanvasVisualizationsService, - CanvasStartDeps ->; - -export const visualizationsServiceFactory: VisualizationsServiceFactory = ({ startPlugins }) => ({ - showNewVisModal: startPlugins.visualizations.showNewVisModal, - getByGroup: startPlugins.visualizations.getByGroup, - getAliases: startPlugins.visualizations.getAliases, -}); diff --git a/x-pack/plugins/canvas/public/services/stubs/embeddables.ts b/x-pack/plugins/canvas/public/services/stubs/embeddables.ts index 9c2cf4d0650abe..173d27563e2b2a 100644 --- a/x-pack/plugins/canvas/public/services/stubs/embeddables.ts +++ b/x-pack/plugins/canvas/public/services/stubs/embeddables.ts @@ -14,5 +14,4 @@ const noop = (..._args: any[]): any => {}; export const embeddablesServiceFactory: EmbeddablesServiceFactory = () => ({ getEmbeddableFactories: noop, - getStateTransfer: noop, }); diff --git a/x-pack/plugins/canvas/public/services/stubs/index.ts b/x-pack/plugins/canvas/public/services/stubs/index.ts index 2216013a29c129..06a5ff49e9c04e 100644 --- a/x-pack/plugins/canvas/public/services/stubs/index.ts +++ b/x-pack/plugins/canvas/public/services/stubs/index.ts @@ -22,7 +22,6 @@ import { navLinkServiceFactory } from './nav_link'; import { notifyServiceFactory } from './notify'; import { platformServiceFactory } from './platform'; import { reportingServiceFactory } from './reporting'; -import { visualizationsServiceFactory } from './visualizations'; import { workpadServiceFactory } from './workpad'; export { customElementServiceFactory } from './custom_element'; @@ -32,7 +31,6 @@ export { navLinkServiceFactory } from './nav_link'; export { notifyServiceFactory } from './notify'; export { platformServiceFactory } from './platform'; export { reportingServiceFactory } from './reporting'; -export { visualizationsServiceFactory } from './visualizations'; export { workpadServiceFactory } from './workpad'; export const pluginServiceProviders: PluginServiceProviders = { @@ -44,7 +42,6 @@ export const pluginServiceProviders: PluginServiceProviders; - -const noop = (..._args: any[]): any => {}; - -export const visualizationsServiceFactory: VisualizationsServiceFactory = () => ({ - showNewVisModal: noop, - getByGroup: noop, - getAliases: noop, -}); diff --git a/x-pack/plugins/canvas/public/services/visualizations.ts b/x-pack/plugins/canvas/public/services/visualizations.ts deleted file mode 100644 index c602b1dd39f3d2..00000000000000 --- a/x-pack/plugins/canvas/public/services/visualizations.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { VisualizationsStart } from '../../../../../src/plugins/visualizations/public'; - -export interface CanvasVisualizationsService { - showNewVisModal: VisualizationsStart['showNewVisModal']; - getByGroup: VisualizationsStart['getByGroup']; - getAliases: VisualizationsStart['getAliases']; -} diff --git a/x-pack/plugins/canvas/public/state/reducers/embeddable.ts b/x-pack/plugins/canvas/public/state/reducers/embeddable.ts index 092d4300d86b79..4cfdc7f21945f6 100644 --- a/x-pack/plugins/canvas/public/state/reducers/embeddable.ts +++ b/x-pack/plugins/canvas/public/state/reducers/embeddable.ts @@ -40,7 +40,7 @@ export const embeddableReducer = handleActions< const element = pageWithElement.elements.find((elem) => elem.id === elementId); - if (!element || element.expression === embeddableExpression) { + if (!element) { return workpadState; } diff --git a/x-pack/plugins/canvas/server/plugin.ts b/x-pack/plugins/canvas/server/plugin.ts index ebe43ba76a46ac..4071b891e4c3d7 100644 --- a/x-pack/plugins/canvas/server/plugin.ts +++ b/x-pack/plugins/canvas/server/plugin.ts @@ -14,7 +14,6 @@ import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { BfetchServerSetup } from 'src/plugins/bfetch/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { HomeServerPluginSetup } from 'src/plugins/home/server'; -import { EmbeddableSetup } from 'src/plugins/embeddable/server'; import { ESSQL_SEARCH_STRATEGY } from '../common/lib/constants'; import { ReportingSetup } from '../../reporting/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; @@ -31,7 +30,6 @@ import { CanvasRouteHandlerContext, createWorkpadRouteContext } from './workpad_ interface PluginsSetup { expressions: ExpressionsServerSetup; - embeddable: EmbeddableSetup; features: FeaturesPluginSetup; home: HomeServerPluginSetup; bfetch: BfetchServerSetup; @@ -84,12 +82,7 @@ export class CanvasPlugin implements Plugin { const kibanaIndex = coreSetup.savedObjects.getKibanaIndex(); registerCanvasUsageCollector(plugins.usageCollection, kibanaIndex); - setupInterpreter(expressionsFork, { - embeddablePersistableStateService: { - extract: plugins.embeddable.extract, - inject: plugins.embeddable.inject, - }, - }); + setupInterpreter(expressionsFork); coreSetup.getStartServices().then(([_, depsStart]) => { const strategy = essqlSearchStrategyProvider(); diff --git a/x-pack/plugins/canvas/server/setup_interpreter.ts b/x-pack/plugins/canvas/server/setup_interpreter.ts index 849ad79717056c..2fe23eb86c086f 100644 --- a/x-pack/plugins/canvas/server/setup_interpreter.ts +++ b/x-pack/plugins/canvas/server/setup_interpreter.ts @@ -7,15 +7,9 @@ import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { functions } from '../canvas_plugin_src/functions/server'; -import { - initFunctions as initExternalFunctions, - InitializeArguments, -} from '../canvas_plugin_src/functions/external'; +import { functions as externalFunctions } from '../canvas_plugin_src/functions/external'; -export function setupInterpreter( - expressions: ExpressionsServerSetup, - dependencies: InitializeArguments -) { +export function setupInterpreter(expressions: ExpressionsServerSetup) { functions.forEach((f) => expressions.registerFunction(f)); - initExternalFunctions(dependencies).forEach((f) => expressions.registerFunction(f)); + externalFunctions.forEach((f) => expressions.registerFunction(f)); } diff --git a/x-pack/plugins/canvas/types/embeddables.ts b/x-pack/plugins/canvas/types/embeddables.ts deleted file mode 100644 index b78efece59d8f8..00000000000000 --- a/x-pack/plugins/canvas/types/embeddables.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TimeRange } from 'src/plugins/data/public'; -import { Filter } from '@kbn/es-query'; -import { EmbeddableInput as Input } from '../../../../src/plugins/embeddable/common/'; - -export type EmbeddableInput = Input & { - timeRange?: TimeRange; - filters?: Filter[]; - savedObjectId?: string; -}; diff --git a/x-pack/plugins/canvas/types/functions.ts b/x-pack/plugins/canvas/types/functions.ts index c80102915ed953..2569e0b10685b7 100644 --- a/x-pack/plugins/canvas/types/functions.ts +++ b/x-pack/plugins/canvas/types/functions.ts @@ -10,8 +10,8 @@ import { UnwrapPromiseOrReturn } from '@kbn/utility-types'; import { functions as commonFunctions } from '../canvas_plugin_src/functions/common'; import { functions as browserFunctions } from '../canvas_plugin_src/functions/browser'; import { functions as serverFunctions } from '../canvas_plugin_src/functions/server'; -import { initFunctions as initExternalFunctions } from '../canvas_plugin_src/functions/external'; -import { initFunctions as initClientFunctions } from '../public/functions'; +import { functions as externalFunctions } from '../canvas_plugin_src/functions/external'; +import { initFunctions } from '../public/functions'; /** * A `ExpressionFunctionFactory` is a powerful type used for any function that produces @@ -90,11 +90,9 @@ export type FunctionFactory = type CommonFunction = FunctionFactory; type BrowserFunction = FunctionFactory; type ServerFunction = FunctionFactory; -type ExternalFunction = FunctionFactory< - ReturnType extends Array ? U : never ->; +type ExternalFunction = FunctionFactory; type ClientFunctions = FunctionFactory< - ReturnType extends Array ? U : never + ReturnType extends Array ? U : never >; /** diff --git a/x-pack/plugins/canvas/types/index.ts b/x-pack/plugins/canvas/types/index.ts index 930f3372920884..09ae1510be6da0 100644 --- a/x-pack/plugins/canvas/types/index.ts +++ b/x-pack/plugins/canvas/types/index.ts @@ -9,7 +9,6 @@ export * from '../../../../src/plugins/expressions/common'; export * from './assets'; export * from './canvas'; export * from './elements'; -export * from './embeddables'; export * from './filters'; export * from './functions'; export * from './renderers'; From cca0a082d2783413d686d4f23a6f60a6ffdd94e7 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Wed, 27 Oct 2021 21:07:08 -0500 Subject: [PATCH 27/46] e2e tests for dependencies (#116369) Also fix a link in testing readme. References #116290. --- x-pack/plugins/apm/ftr_e2e/README.md | 2 +- .../read_only_user/dependencies.spec.ts | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts diff --git a/x-pack/plugins/apm/ftr_e2e/README.md b/x-pack/plugins/apm/ftr_e2e/README.md index 2df4e837d2e555..96d6671bb36990 100644 --- a/x-pack/plugins/apm/ftr_e2e/README.md +++ b/x-pack/plugins/apm/ftr_e2e/README.md @@ -4,4 +4,4 @@ APM uses [FTR](../../../../packages/kbn-test/README.md) (functional test runner) ## Running tests -Go to [tests documentation](../scripts/test#e2e-tests-cypress/README.md) \ No newline at end of file +Go to [tests documentation](../dev_docs/testing.md#e2e-tests-cypress) diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts new file mode 100644 index 00000000000000..0ab2d5682a900c --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +const timeRange = { + rangeFrom: Cypress.env('START_DATE'), + rangeTo: Cypress.env('END_DATE'), +}; + +describe('Dependencies', () => { + beforeEach(() => { + cy.loginAsReadOnlyUser(); + }); + + describe('top-level dependencies page', () => { + it('has a list of dependencies and you can navigate to the page for one', () => { + cy.visit(`/app/apm/services?${new URLSearchParams(timeRange)}`); + cy.contains('nav a', 'Dependencies').click(); + + // `force: true` because Cypress says the element is 0x0 + cy.contains('postgresql').click({ force: true }); + + cy.contains('h1', 'postgresql'); + }); + }); + + describe('dependency overview page', () => { + it('shows dependency information and you can navigate to a page for an upstream service', () => { + cy.visit( + `/app/apm/backends/overview?${new URLSearchParams({ + ...timeRange, + backendName: 'postgresql', + })}` + ); + + cy.get('[data-test-subj="latencyChart"]'); + cy.get('[data-test-subj="throughputChart"]'); + cy.get('[data-test-subj="errorRateChart"]'); + + cy.contains('opbeans-python').click({ force: true }); + + cy.contains('h1', 'opbeans-python'); + }); + }); + + describe('service overview page', () => { + it('shows dependency information and you can navigate to a page for a dependency', () => { + cy.visit( + `/app/apm/services/opbeans-python/overview?${new URLSearchParams( + timeRange + )}` + ); + + cy.contains('postgresql').click({ force: true }); + + cy.contains('h1', 'postgresql'); + }); + }); + + describe('service dependencies tab', () => { + it('shows dependency information and you can navigate to a page for a dependency', () => { + cy.visit( + `/app/apm/services/opbeans-python/overview?${new URLSearchParams( + timeRange + )}` + ); + + cy.contains('a[role="tab"]', 'Dependencies').click(); + + cy.contains('Time spent by dependency'); + + cy.contains('postgresql').click({ force: true }); + + cy.contains('h1', 'postgresql'); + }); + }); +}); From a78e50ca94523688484d0327f76a656bfb4c1078 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 28 Oct 2021 03:13:46 +0100 Subject: [PATCH 28/46] skip flaky suite (#116059) --- .../test_suites/saved_objects_management/hidden_types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts b/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts index 8e7adb504ebee3..b384c3fbbbb1ec 100644 --- a/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts +++ b/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts @@ -68,7 +68,8 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide }); }); - describe('Delete modal', () => { + // FLAKY: https://github.com/elastic/kibana/issues/116059 + describe.skip('Delete modal', () => { it('should display a warning then trying to delete hidden saved objects', async () => { await PageObjects.savedObjects.clickCheckboxByTitle('A Pie'); await PageObjects.savedObjects.clickCheckboxByTitle('A Dashboard'); From 3424f94fb4421c050930c90c59260b070440c0c1 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Wed, 27 Oct 2021 21:31:18 -0600 Subject: [PATCH 29/46] Unskips tests and fixes a few of them. (#116469) ## Summary * For the test of `create_endpoint_exceptions.ts` code was changed to try to reduce/remove flake. I put the code back to where it was and with the sorting introduced it all should pass * Added `await waitForSignalsToBePresent(supertest, 1, [id]);` to areas of code that were missing. * The `.flat` should be an added layer of protection for flakes. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../tests/create_endpoint_exceptions.ts | 25 +++++++++---------- .../ip_array.ts | 1 + .../exception_operators_data_types/text.ts | 14 +++-------- .../text_array.ts | 2 ++ 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts index d5e623989b4606..6c6fcc366782af 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts @@ -70,8 +70,7 @@ export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - // Flaky - describe.skip('Rule exception operators for endpoints', () => { + describe('Rule exception operators for endpoints', () => { before(async () => { await esArchiver.load( 'x-pack/test/functional/es_archives/rule_exceptions/endpoint_without_host_type' @@ -113,7 +112,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, @@ -135,7 +134,7 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Macos' }, }, { os: { name: 'Windows' }, @@ -174,7 +173,7 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Macos' }, }, { os: { name: 'Windows' }, @@ -210,7 +209,7 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Macos' }, }, { os: { name: 'Windows' }, @@ -336,7 +335,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, @@ -372,7 +371,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, @@ -501,10 +500,10 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { type: 'macos' }, }, { - os: { type: 'windows' }, + os: { name: 'Macos' }, }, { os: { type: 'windows' }, @@ -546,10 +545,10 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { type: 'macos' }, }, { - os: { type: 'windows' }, + os: { name: 'Macos' }, }, { os: { type: 'windows' }, @@ -876,7 +875,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts index 89f7693e723589..efff288fddac28 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts @@ -508,6 +508,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); expect(ips.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts index 9fd733789588fa..ff2f6806540470 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts @@ -426,9 +426,7 @@ export default ({ getService }: FtrProviderContext) => { expect(hits).to.eql(['word four']); }); - // This test is unreliable due to a race condition... we don't know if the rule ran and - // generated 0 signals, or if the index hasn't refreshed yet. - it.skip('should filter 4 text if all are set as exceptions', async () => { + it('should filter 4 text if all are set as exceptions', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -448,9 +446,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('"is not one of" operator', () => { - // This test is unreliable due to a race condition... we don't know if the rule ran and - // generated 0 signals, or if the index hasn't refreshed yet. - it.skip('will return 0 results if it cannot find what it is excluding', async () => { + it('will return 0 results if it cannot find what it is excluding', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -489,9 +485,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('"exists" operator', () => { - // This test is unreliable due to a race condition... we don't know if the rule ran and - // generated 0 signals, or if the index hasn't refreshed yet. - it.skip('will return 0 results if matching against text', async () => { + it('will return 0 results if matching against text', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -577,7 +571,7 @@ export default ({ getService }: FtrProviderContext) => { expect(hits).to.eql(['four', 'two']); }); - it.skip('will return 0 results if we have a list that includes all text', async () => { + it('will return 0 results if we have a list that includes all text', async () => { await importTextFile( supertest, 'text', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts index 3bcf8692d58f94..6d251246671329 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts @@ -337,6 +337,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); @@ -515,6 +516,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); From 2b15dc9b10a370a3d65b039c3d78eedfbb8a2bec Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 28 Oct 2021 09:36:52 +0200 Subject: [PATCH 30/46] retry reading suggestion list (#116405) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/functional/apps/visualize/_timelion.ts | 3 +-- test/functional/page_objects/timelion_page.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/test/functional/apps/visualize/_timelion.ts b/test/functional/apps/visualize/_timelion.ts index 712edb440311ff..631d2148d73c39 100644 --- a/test/functional/apps/visualize/_timelion.ts +++ b/test/functional/apps/visualize/_timelion.ts @@ -257,8 +257,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(value).to.eql('.es()'); }); - // FLAKY: https://github.com/elastic/kibana/issues/116033 - describe.skip('dynamic suggestions for argument values', () => { + describe('dynamic suggestions for argument values', () => { describe('.es()', () => { it('should show index pattern suggestions for index argument', async () => { await monacoEditor.setCodeEditorValue(''); diff --git a/test/functional/page_objects/timelion_page.ts b/test/functional/page_objects/timelion_page.ts index bdfde3c8145e5e..ba1db60bc6350d 100644 --- a/test/functional/page_objects/timelion_page.ts +++ b/test/functional/page_objects/timelion_page.ts @@ -7,13 +7,21 @@ */ import { FtrService } from '../ftr_provider_context'; +import type { WebElementWrapper } from '../services/lib/web_element_wrapper'; export class TimelionPageObject extends FtrService { private readonly testSubjects = this.ctx.getService('testSubjects'); + private readonly retry = this.ctx.getService('retry'); public async getSuggestionItemsText() { - const timelionCodeEditor = await this.testSubjects.find('timelionCodeEditor'); - const lists = await timelionCodeEditor.findAllByClassName('monaco-list-row'); + let lists: WebElementWrapper[] = []; + await this.retry.try(async () => { + const timelionCodeEditor = await this.testSubjects.find('timelionCodeEditor'); + lists = await timelionCodeEditor.findAllByClassName('monaco-list-row'); + if (lists.length === 0) { + throw new Error('suggestion list not populated'); + } + }); return await Promise.all(lists.map(async (element) => await element.getVisibleText())); } From 01503c5d5d52e8db502dbe15e784a340ea91b713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Thu, 28 Oct 2021 10:14:40 +0200 Subject: [PATCH 31/46] Add stale bot for APM issues (#116432) * Add stale bot for APM issues * Update stale.yml * Update stale.yml --- .github/stale.yml | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000000000..31bd822640359d --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,58 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 180 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: false + +# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) +onlyLabels: ["Team:apm"] + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: ["technical debt", "prevent stale"] + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false + +# Label to use when marking as stale +staleLabel: stale + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Comment to post when closing a stale Issue or Pull Request. +# closeComment: > +# Your comment here. + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +# Limit to only `issues` or `pulls` +only: issues + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +# pulls: +# daysUntilStale: 30 +# markComment: > +# This pull request has been automatically marked as stale because it has not had +# recent activity. It will be closed if no further activity occurs. Thank you +# for your contributions. + +# issues: +# exemptLabels: +# - confirmed From 2e4c87211cd9b6b2ab9fd11fac19ecd5c34fcb09 Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Thu, 28 Oct 2021 10:36:57 +0200 Subject: [PATCH 32/46] [docs] Fix anchor link to saved objects migrations testing (#116427) --- dev_docs/tutorials/saved_objects.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_docs/tutorials/saved_objects.mdx b/dev_docs/tutorials/saved_objects.mdx index 29a0b60983d90c..9583e195d1c828 100644 --- a/dev_docs/tutorials/saved_objects.mdx +++ b/dev_docs/tutorials/saved_objects.mdx @@ -254,4 +254,4 @@ the error should be verbose and informative so that the corrupt document can be ### Testing Migrations -Bugs in a migration function cause downtime for our users and therefore have a very high impact. Follow the . +Bugs in a migration function cause downtime for our users and therefore have a very high impact. Follow the . From 42857a4da9d794d48bf60219a3450a789a5df2d8 Mon Sep 17 00:00:00 2001 From: spalger Date: Thu, 28 Oct 2021 09:03:16 +0000 Subject: [PATCH 33/46] skip flaky suite (#116537) --- x-pack/test/examples/search_examples/search_sessions_cache.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/examples/search_examples/search_sessions_cache.ts b/x-pack/test/examples/search_examples/search_sessions_cache.ts index 7e52849ed2a7e1..0da2de46a1f628 100644 --- a/x-pack/test/examples/search_examples/search_sessions_cache.ts +++ b/x-pack/test/examples/search_examples/search_sessions_cache.ts @@ -27,7 +27,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return text; } - describe('Search session client side cache', () => { + // FLAKY: https://github.com/elastic/kibana/issues/116537 + describe.skip('Search session client side cache', () => { const appId = 'searchExamples'; before(async function () { From e0764bd1d00067d0cdaa5378a7646b5f0967cad3 Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Thu, 28 Oct 2021 10:04:01 +0100 Subject: [PATCH 34/46] Update examples in the elastic-apm-synthtrace CLI section (#116574) --- packages/elastic-apm-synthtrace/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/elastic-apm-synthtrace/README.md b/packages/elastic-apm-synthtrace/README.md index 3d65120b4b6c2c..cdbd536831676b 100644 --- a/packages/elastic-apm-synthtrace/README.md +++ b/packages/elastic-apm-synthtrace/README.md @@ -93,10 +93,10 @@ const esEvents = toElasticsearchOutput([ Via the CLI, you can upload scenarios, either using a fixed time range or continuously generating data. Some examples are available in in `src/scripts/examples`. Here's an example for live data: -`$ node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-generator/src/examples/01_simple_trace.ts --target=http://admin:changeme@localhost:9200 --live` +`$ node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts --target=http://admin:changeme@localhost:9200 --live` For a fixed time window: -`$ node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-generator/src/examples/01_simple_trace.ts --target=http://admin:changeme@localhost:9200 --from=now-24h --to=now` +`$ node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts --target=http://admin:changeme@localhost:9200 --from=now-24h --to=now` The script will try to automatically find bootstrapped APM indices. __If these indices do not exist, the script will exit with an error. It will not bootstrap the indices itself.__ From c760db6a010cf05dca0aae52fea4646635e41d4f Mon Sep 17 00:00:00 2001 From: spalger Date: Thu, 28 Oct 2021 09:08:39 +0000 Subject: [PATCH 35/46] skip flaky suite (#115307) --- .../edit_policy/form_validation/timing.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts index be4f99103b319e..4c553cac02303b 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts @@ -12,7 +12,8 @@ import { PhaseWithTiming } from '../../../../common/types'; import { setupEnvironment } from '../../helpers'; import { setupValidationTestBed, ValidationTestBed } from './validation.helpers'; -describe(' timing validation', () => { +// FLAKY: https://github.com/elastic/kibana/issues/115307 +describe.skip(' timing validation', () => { let testBed: ValidationTestBed; let actions: ValidationTestBed['actions']; const { server, httpRequestsMockHelpers } = setupEnvironment(); From 8a34170b2f5e4862d833cb66087869c8d13d6bf9 Mon Sep 17 00:00:00 2001 From: spalger Date: Thu, 28 Oct 2021 09:13:26 +0000 Subject: [PATCH 36/46] skip flaky suite (#90536) --- .../usage_collection/test_suites/application_usage/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/usage_collection/test_suites/application_usage/index.ts b/x-pack/test/usage_collection/test_suites/application_usage/index.ts index fc53c8ddf5ed37..4ba45b4bf9e126 100644 --- a/x-pack/test/usage_collection/test_suites/application_usage/index.ts +++ b/x-pack/test/usage_collection/test_suites/application_usage/index.ts @@ -10,7 +10,8 @@ import { FtrProviderContext } from '../../ftr_provider_context'; import { applicationUsageSchema } from '../../../../../src/plugins/kibana_usage_collection/server/collectors/application_usage/schema'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - describe('Application Usage', function () { + // FLAKY: https://github.com/elastic/kibana/issues/90536 + describe.skip('Application Usage', function () { this.tags('ciGroup1'); const { common } = getPageObjects(['common']); const browser = getService('browser'); From acf04261945abc4cb156df9da842d3e5a352ab9a Mon Sep 17 00:00:00 2001 From: spalger Date: Thu, 28 Oct 2021 09:19:16 +0000 Subject: [PATCH 37/46] skip flaky suite (#89369) --- .../plugins/reporting/server/routes/diagnostic/browser.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts b/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts index 7677f37702f0d8..7b4cc2008a6769 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts @@ -28,7 +28,8 @@ type SetupServerReturn = UnwrapPromise>; const devtoolMessage = 'DevTools listening on (ws://localhost:4000)'; const fontNotFoundMessage = 'Could not find the default font'; -describe('POST /diagnose/browser', () => { +// FLAKY: https://github.com/elastic/kibana/issues/89369 +describe.skip('POST /diagnose/browser', () => { jest.setTimeout(6000); const reportingSymbol = Symbol('reporting'); const mockLogger = createMockLevelLogger(); From 4a92e3bc360ffa3b2cc1c5109c16b9fba263f7cd Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 28 Oct 2021 12:36:27 +0200 Subject: [PATCH 38/46] [Exploratory View]added loading state for metric selector (#115748) Co-authored-by: Dominique Clarke --- .../series_editor/report_metric_options.tsx | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx index 496e7a10f9c447..eca18f0eb0dd40 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx @@ -13,6 +13,8 @@ import { EuiListGroup, EuiListGroupItem, EuiBadge, + EuiText, + EuiLoadingSpinner, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -33,7 +35,7 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { const [showOptions, setShowOptions] = useState(false); const metricOptions = seriesConfig?.metricOptions; - const { indexPatterns } = useAppIndexPatternContext(); + const { indexPatterns, loading } = useAppIndexPatternContext(); const onChange = (value?: string) => { setSeries(seriesId, { @@ -78,6 +80,10 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { }; }); + if (!indexPattern && !loading) { + return {NO_DATA_AVAILABLE}; + } + return ( <> {!series.selectedMetricField && ( @@ -88,6 +94,7 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { onClick={() => setShowOptions((prevState) => !prevState)} fill size="s" + isLoading={!indexPattern && loading} > {SELECT_REPORT_METRIC_LABEL} @@ -107,19 +114,23 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { )} - {series.selectedMetricField && ( - onChange(undefined)} - iconOnClickAriaLabel={REMOVE_REPORT_METRIC_LABEL} - > - { - seriesConfig?.metricOptions?.find((option) => option.id === series.selectedMetricField) - ?.label - } - - )} + {series.selectedMetricField && + (indexPattern && !loading ? ( + onChange(undefined)} + iconOnClickAriaLabel={REMOVE_REPORT_METRIC_LABEL} + > + { + seriesConfig?.metricOptions?.find( + (option) => option.id === series.selectedMetricField + )?.label + } + + ) : ( + + ))} ); } @@ -137,3 +148,7 @@ const REMOVE_REPORT_METRIC_LABEL = i18n.translate( defaultMessage: 'Remove report metric', } ); + +const NO_DATA_AVAILABLE = i18n.translate('xpack.observability.expView.seriesEditor.noData', { + defaultMessage: 'No data available', +}); From f79b94faedaf429063e6e56395c0ca6fc0562f9e Mon Sep 17 00:00:00 2001 From: Dzmitry Lemechko Date: Thu, 28 Oct 2021 13:11:24 +0200 Subject: [PATCH 39/46] [load testing] add default package to simulation class (#116375) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/test/load/runner.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/test/load/runner.ts b/x-pack/test/load/runner.ts index 0bea5992f55394..c48a8e33d6eef6 100644 --- a/x-pack/test/load/runner.ts +++ b/x-pack/test/load/runner.ts @@ -28,7 +28,11 @@ if (!Fs.existsSync(gatlingProjectRootPath)) { ); } -const dropEmptyLines = (s: string) => s.split(',').filter((i) => i.length > 0); +const dropEmptyLines = (s: string) => + s + .split(',') + .filter((i) => i.length > 0) + .map((i) => (i.includes('.') ? i : `branch.${i}`)); const simulationClasses = dropEmptyLines(simulationEntry); const simulationsRootPath = resolve(gatlingProjectRootPath, baseSimulationPath); From 0e1d42d4004a3116a4a985b2dbf86543eadb0019 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Thu, 28 Oct 2021 13:11:36 +0200 Subject: [PATCH 40/46] skips exceptions tests (#116568) --- .../cypress/integration/exceptions/from_alert.spec.ts | 2 +- .../cypress/integration/exceptions/from_rule.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts index 84ad93fa089434..cea290eeef17be 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts @@ -35,7 +35,7 @@ import { import { ALERTS_URL } from '../../urls/navigation'; import { cleanKibana } from '../../tasks/common'; -describe('From alert', () => { +describe.skip('From alert', () => { const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; beforeEach(() => { diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts index ea6b6cf0186b49..4af6467e5d33c2 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts @@ -35,7 +35,7 @@ import { refreshPage } from '../../tasks/security_header'; import { ALERTS_URL } from '../../urls/navigation'; import { cleanKibana } from '../../tasks/common'; -describe('From rule', () => { +describe.skip('From rule', () => { const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1'; beforeEach(() => { cleanKibana(); From 21cd11a85195ec26ec85823086e8edd0dcfe7002 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Thu, 28 Oct 2021 06:24:52 -0500 Subject: [PATCH 41/46] [ML] Fix Transforms not retaining _meta on clone (#116206) * [ML] Retain _meta on clone * [ML] Fix validation on schema to only check it it's defined/not null * [ML] Remove validation because es should handle the validation already * Change type to unknown --- .../transform/common/api_schemas/transforms.ts | 12 ++++++++++++ x-pack/plugins/transform/common/types/transform.ts | 1 + .../plugins/transform/public/app/common/request.ts | 1 + .../components/step_details/common.ts | 5 +++++ .../components/step_details/step_details_form.tsx | 1 + 5 files changed, 20 insertions(+) diff --git a/x-pack/plugins/transform/common/api_schemas/transforms.ts b/x-pack/plugins/transform/common/api_schemas/transforms.ts index 8867ecb5cc760e..55ea326069f0dd 100644 --- a/x-pack/plugins/transform/common/api_schemas/transforms.ts +++ b/x-pack/plugins/transform/common/api_schemas/transforms.ts @@ -94,6 +94,13 @@ function transformConfigPayloadValidator< } } +export const _metaSchema = schema.object( + {}, + { + unknowns: 'allow', + } +); + // PUT transforms/{transformId} export const putTransformsRequestSchema = schema.object( { @@ -112,6 +119,11 @@ export const putTransformsRequestSchema = schema.object( settings: schema.maybe(settingsSchema), source: sourceSchema, sync: schema.maybe(syncSchema), + /** + * This _meta field stores an arbitrary key-value map + * where keys are strings and values are arbitrary objects (possibly also maps). + */ + _meta: schema.maybe(_metaSchema), }, { validate: transformConfigPayloadValidator, diff --git a/x-pack/plugins/transform/common/types/transform.ts b/x-pack/plugins/transform/common/types/transform.ts index a478946ff917c5..92ffc0b99bc3dd 100644 --- a/x-pack/plugins/transform/common/types/transform.ts +++ b/x-pack/plugins/transform/common/types/transform.ts @@ -24,6 +24,7 @@ export type TransformBaseConfig = PutTransformsRequestSchema & { create_time?: number; version?: string; alerting_rules?: TransformHealthAlertRule[]; + _meta?: Record; }; export interface PivotConfigDefinition { diff --git a/x-pack/plugins/transform/public/app/common/request.ts b/x-pack/plugins/transform/public/app/common/request.ts index 8f8341260bd7eb..184e3d31e89d21 100644 --- a/x-pack/plugins/transform/public/app/common/request.ts +++ b/x-pack/plugins/transform/public/app/common/request.ts @@ -242,6 +242,7 @@ export const getCreateTransformRequestBody = ( }, } : {}), + ...(transformDetailsState._meta ? { _meta: transformDetailsState._meta } : {}), // conditionally add additional settings ...getCreateTransformSettingsRequestBody(transformDetailsState), }); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts index 39b1a2de26f8ec..21e6bce204ec85 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts @@ -27,6 +27,7 @@ export interface StepDetailsExposedState { transformSettingsDocsPerSecond?: number; valid: boolean; indexPatternTimeField?: string | undefined; + _meta?: Record; } const defaultContinuousModeDelay = '60s'; @@ -94,6 +95,10 @@ export function applyTransformConfigToDetailsState( state.transformSettingsDocsPerSecond = transformConfig.settings.docs_per_second; } } + + if (transformConfig._meta) { + state._meta = transformConfig._meta; + } } return state; } diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx index 416bad15d800a6..eda95013f60bd1 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx @@ -289,6 +289,7 @@ export const StepDetailsForm: FC = React.memo( touched: true, valid, indexPatternTimeField, + _meta: defaults._meta, }); // custom comparison /* eslint-disable react-hooks/exhaustive-deps */ From f598c4343546f18a14c5b0cc10ec28fdf90afc56 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Thu, 28 Oct 2021 12:40:33 +0100 Subject: [PATCH 42/46] [Fleet] Add missing documentation link on integrations page (#116453) * add missing doc link * node scripts/check_published_api_changes.js --accept --- ...-plugin-core-public.doclinksstart.links.md | 1 + ...kibana-plugin-core-public.doclinksstart.md | 2 +- .../public/doc_links/doc_links_service.ts | 2 + src/core/public/public.api.md | 1 + .../epm/components/integration_preference.tsx | 39 +++++++++++-------- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index ed6763db69ffe4..4e44df9d4e1835 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -230,6 +230,7 @@ readonly links: { readonly ingest: Record; readonly fleet: Readonly<{ datastreamsILM: string; + beatsAgentComparison: string; guide: string; fleetServer: string; fleetServerAddFleetServer: string; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 96c2c0df9d7822..5871a84c5402ed 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly elasticStackGetStarted: string;
readonly apm: {
readonly kibanaSettings: string;
readonly supportedServiceMaps: string;
readonly customLinks: string;
readonly droppedTransactionSpans: string;
readonly upgrading: string;
readonly metaData: string;
};
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
readonly troubleshootGaps: string;
};
readonly securitySolution: {
readonly trustedApps: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Readonly<{
guide: string;
infrastructureThreshold: string;
logsThreshold: string;
metricsThreshold: string;
monitorStatus: string;
monitorUptime: string;
tlsCertificate: string;
uptimeDurationAnomaly: string;
}>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
elasticsearchEnableApiKeys: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly spaces: Readonly<{
kibanaLegacyUrlAliases: string;
kibanaDisableLegacyUrlAliasesApi: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
datastreamsILM: string;
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
installElasticAgent: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
learnMoreBlog: string;
apiKeysLearnMore: string;
}>;
readonly ecs: {
readonly guide: string;
};
readonly clients: {
readonly guide: string;
readonly goOverview: string;
readonly javaIndex: string;
readonly jsIntro: string;
readonly netGuide: string;
readonly perlGuide: string;
readonly phpGuide: string;
readonly pythonGuide: string;
readonly rubyOverview: string;
readonly rustGuide: string;
};
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly elasticStackGetStarted: string;
readonly apm: {
readonly kibanaSettings: string;
readonly supportedServiceMaps: string;
readonly customLinks: string;
readonly droppedTransactionSpans: string;
readonly upgrading: string;
readonly metaData: string;
};
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
readonly troubleshootGaps: string;
};
readonly securitySolution: {
readonly trustedApps: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Readonly<{
guide: string;
infrastructureThreshold: string;
logsThreshold: string;
metricsThreshold: string;
monitorStatus: string;
monitorUptime: string;
tlsCertificate: string;
uptimeDurationAnomaly: string;
}>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
elasticsearchEnableApiKeys: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly spaces: Readonly<{
kibanaLegacyUrlAliases: string;
kibanaDisableLegacyUrlAliasesApi: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
datastreamsILM: string;
beatsAgentComparison: string;
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
installElasticAgent: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
learnMoreBlog: string;
apiKeysLearnMore: string;
}>;
readonly ecs: {
readonly guide: string;
};
readonly clients: {
readonly guide: string;
readonly goOverview: string;
readonly javaIndex: string;
readonly jsIntro: string;
readonly netGuide: string;
readonly perlGuide: string;
readonly phpGuide: string;
readonly pythonGuide: string;
readonly rubyOverview: string;
readonly rustGuide: string;
};
} | | diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 2bbb4703ecd19d..0cab7a72adae8b 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -479,6 +479,7 @@ export class DocLinksService { settingsFleetServerHostSettings: `${FLEET_DOCS}fleet-settings.html#fleet-server-hosts-setting`, troubleshooting: `${FLEET_DOCS}fleet-troubleshooting.html`, elasticAgent: `${FLEET_DOCS}elastic-agent-installation.html`, + beatsAgentComparison: `${FLEET_DOCS}beats-agent-comparison.html`, datastreams: `${FLEET_DOCS}data-streams.html`, datastreamsILM: `${FLEET_DOCS}data-streams.html#data-streams-ilm`, datastreamsNamingScheme: `${FLEET_DOCS}data-streams.html#data-streams-naming-scheme`, @@ -736,6 +737,7 @@ export interface DocLinksStart { readonly ingest: Record; readonly fleet: Readonly<{ datastreamsILM: string; + beatsAgentComparison: string; guide: string; fleetServer: string; fleetServerAddFleetServer: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index cf0b526aa9fd92..5d63d2b6f77cea 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -697,6 +697,7 @@ export interface DocLinksStart { readonly ingest: Record; readonly fleet: Readonly<{ datastreamsILM: string; + beatsAgentComparison: string; guide: string; fleetServer: string; fleetServerAddFleetServer: string; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx index 9c9027fb94ac52..fa9b9a7ed190e0 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx @@ -22,6 +22,8 @@ import { EuiFlexItem, } from '@elastic/eui'; +import { useStartServices } from '../../../hooks'; + export type IntegrationPreferenceType = 'recommended' | 'beats' | 'agent'; interface Option { @@ -34,23 +36,6 @@ export interface Props { onChange: (type: IntegrationPreferenceType) => void; } -const link = ( - - - -); - -const title = ( - -); - const recommendedTooltip = ( { const [idSelected, setIdSelected] = React.useState(initialType); + + const { docLinks } = useStartServices(); + + const link = ( + + + + ); + + const title = ( + + ); + const radios = options.map((option) => ({ id: option.type, value: option.type, From 5338e1965f286fc2b54dde3e52c78bb0150c55ba Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 28 Oct 2021 14:47:36 +0300 Subject: [PATCH 43/46] [Cases] Fix configure cases flaky test (#116575) --- .../components/configure_cases/index.test.tsx | 242 +++++++++--------- 1 file changed, 121 insertions(+), 121 deletions(-) diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx index 1fed1d90689be8..990d44584cf055 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx @@ -47,6 +47,7 @@ describe('ConfigureCases', () => { iconClass: 'logoSecurity', }); }); + beforeEach(() => { useActionTypesMock.mockImplementation(() => useActionTypesResponse); }); @@ -451,150 +452,149 @@ describe('ConfigureCases', () => { ).toBe('Update My Connector 2'); }); }); -}); -// Failing: See https://github.com/elastic/kibana/issues/115366 -describe.skip('closure options', () => { - let wrapper: ReactWrapper; - let persistCaseConfigure: jest.Mock; + describe('closure options', () => { + let wrapper: ReactWrapper; + let persistCaseConfigure: jest.Mock; - beforeEach(() => { - persistCaseConfigure = jest.fn(); - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - mapping: null, - closureType: 'close-by-user', - connector: { - id: 'servicenow-1', - name: 'My connector', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - currentConfiguration: { + beforeEach(() => { + persistCaseConfigure = jest.fn(); + useCaseConfigureMock.mockImplementation(() => ({ + ...useCaseConfigureResponse, + mapping: null, + closureType: 'close-by-user', connector: { - id: 'My connector', + id: 'servicenow-1', name: 'My connector', - type: ConnectorTypes.jira, + type: ConnectorTypes.serviceNowITSM, fields: null, }, - closureType: 'close-by-user', - }, - persistCaseConfigure, - })); - useConnectorsMock.mockImplementation(() => useConnectorsResponse); - useGetUrlSearchMock.mockImplementation(() => searchURL); + currentConfiguration: { + connector: { + id: 'My connector', + name: 'My connector', + type: ConnectorTypes.jira, + fields: null, + }, + closureType: 'close-by-user', + }, + persistCaseConfigure, + })); + useConnectorsMock.mockImplementation(() => useConnectorsResponse); + useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { - wrappingComponent: TestProviders, + wrapper = mount(, { + wrappingComponent: TestProviders, + }); }); - }); - test('it submits the configuration correctly when changing closure type', () => { - wrapper.find('input[id="close-by-pushing"]').simulate('change'); - wrapper.update(); + test('it submits the configuration correctly when changing closure type', () => { + wrapper.find('input[id="close-by-pushing"]').simulate('change'); + wrapper.update(); - expect(persistCaseConfigure).toHaveBeenCalled(); - expect(persistCaseConfigure).toHaveBeenCalledWith({ - connector: { - id: 'servicenow-1', - name: 'My connector', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - closureType: 'close-by-pushing', + expect(persistCaseConfigure).toHaveBeenCalled(); + expect(persistCaseConfigure).toHaveBeenCalledWith({ + connector: { + id: 'servicenow-1', + name: 'My connector', + type: ConnectorTypes.serviceNowITSM, + fields: null, + }, + closureType: 'close-by-pushing', + }); }); }); -}); -describe('user interactions', () => { - beforeEach(() => { - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - mapping: null, - closureType: 'close-by-user', - connector: { - id: 'resilient-2', - name: 'unchanged', - type: ConnectorTypes.resilient, - fields: null, - }, - currentConfiguration: { + describe('user interactions', () => { + beforeEach(() => { + useCaseConfigureMock.mockImplementation(() => ({ + ...useCaseConfigureResponse, + mapping: null, + closureType: 'close-by-user', connector: { id: 'resilient-2', name: 'unchanged', - type: ConnectorTypes.serviceNowITSM, + type: ConnectorTypes.resilient, fields: null, }, - closureType: 'close-by-user', - }, - })); - useConnectorsMock.mockImplementation(() => useConnectorsResponse); - useGetUrlSearchMock.mockImplementation(() => searchURL); - }); - - test('it show the add flyout when pressing the add connector button', async () => { - const wrapper = mount(, { - wrappingComponent: TestProviders, + currentConfiguration: { + connector: { + id: 'resilient-2', + name: 'unchanged', + type: ConnectorTypes.serviceNowITSM, + fields: null, + }, + closureType: 'close-by-user', + }, + })); + useConnectorsMock.mockImplementation(() => useConnectorsResponse); + useGetUrlSearchMock.mockImplementation(() => searchURL); }); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.find('button[data-test-subj="dropdown-connector-add-connector"]').simulate('click'); + test('it show the add flyout when pressing the add connector button', async () => { + const wrapper = mount(, { + wrappingComponent: TestProviders, + }); - await waitFor(() => { - wrapper.update(); - expect(wrapper.find('ConnectorAddFlyout').exists()).toBe(true); - expect(wrapper.find('ConnectorAddFlyout').prop('actionTypes')).toEqual([ - expect.objectContaining({ - id: '.servicenow', - }), - expect.objectContaining({ - id: '.jira', - }), - expect.objectContaining({ - id: '.resilient', - }), - expect.objectContaining({ - id: '.servicenow-sir', - }), - ]); + wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); + wrapper.find('button[data-test-subj="dropdown-connector-add-connector"]').simulate('click'); + + await waitFor(() => { + wrapper.update(); + expect(wrapper.find('ConnectorAddFlyout').exists()).toBe(true); + expect(wrapper.find('ConnectorAddFlyout').prop('actionTypes')).toEqual([ + expect.objectContaining({ + id: '.servicenow', + }), + expect.objectContaining({ + id: '.jira', + }), + expect.objectContaining({ + id: '.resilient', + }), + expect.objectContaining({ + id: '.servicenow-sir', + }), + ]); + }); }); - }); - test('it show the edit flyout when pressing the update connector button', async () => { - const actionType = actionTypeRegistryMock.createMockActionTypeModel({ - id: '.resilient', - validateConnector: () => { - return Promise.resolve({}); - }, - validateParams: () => { - const validationResult = { errors: {} }; - return Promise.resolve(validationResult); - }, - actionConnectorFields: null, - }); - - useKibanaMock().services.triggersActionsUi.actionTypeRegistry.get = jest - .fn() - .mockReturnValue(actionType); - useKibanaMock().services.triggersActionsUi.actionTypeRegistry.has = jest - .fn() - .mockReturnValue(true); - - const wrapper = mount(, { - wrappingComponent: TestProviders, - }); - wrapper - .find('button[data-test-subj="case-configure-update-selected-connector-button"]') - .simulate('click'); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find('ConnectorEditFlyout').exists()).toBe(true); - expect(wrapper.find('ConnectorEditFlyout').prop('initialConnector')).toEqual(connectors[1]); - }); + test('it show the edit flyout when pressing the update connector button', async () => { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ + id: '.resilient', + validateConnector: () => { + return Promise.resolve({}); + }, + validateParams: () => { + const validationResult = { errors: {} }; + return Promise.resolve(validationResult); + }, + actionConnectorFields: null, + }); + + useKibanaMock().services.triggersActionsUi.actionTypeRegistry.get = jest + .fn() + .mockReturnValue(actionType); + useKibanaMock().services.triggersActionsUi.actionTypeRegistry.has = jest + .fn() + .mockReturnValue(true); + + const wrapper = mount(, { + wrappingComponent: TestProviders, + }); + wrapper + .find('button[data-test-subj="case-configure-update-selected-connector-button"]') + .simulate('click'); + + await waitFor(() => { + wrapper.update(); + expect(wrapper.find('ConnectorEditFlyout').exists()).toBe(true); + expect(wrapper.find('ConnectorEditFlyout').prop('initialConnector')).toEqual(connectors[1]); + }); - expect( - wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() - ).toBeFalsy(); + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeFalsy(); + }); }); }); From 9b14bab5dc4f85a882c3ed69fa40bb4034948c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yulia=20=C4=8Cech?= <6585477+yuliacech@users.noreply.github.com> Date: Thu, 28 Oct 2021 13:54:09 +0200 Subject: [PATCH 44/46] [ILM] Fixed skipped a11y and functional tests (#116207) * [ILM] Fixed ILM a11y test by creating a snapshot repo that is now required in the ILM API * [ILM] Fixed functional test by creating a snapshot repository * [ILM] Updated the params after es client update * [ILM] Added filtering by policy name to the a11y test to find the correct ILM policy in the list * [ILM] Added filtering by policy name to the a11y test to find the correct ILM policy in the list Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../policy_list/components/policy_table.tsx | 4 ++- .../apps/index_lifecycle_management.ts | 30 +++++++++++++++---- .../index_lifecycle_management/home_page.ts | 17 +++++++++-- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/components/policy_table.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/components/policy_table.tsx index d6d030c3ec7339..61ce87860d8972 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/components/policy_table.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/components/policy_table.tsx @@ -191,7 +191,9 @@ export const PolicyTable: React.FunctionComponent = ({ policies }) => { direction: 'asc', }, }} - search={{ box: { incremental: true } }} + search={{ + box: { incremental: true, 'data-test-subj': 'ilmSearchBar' }, + }} tableLayout="auto" items={policies} columns={columns} diff --git a/x-pack/test/accessibility/apps/index_lifecycle_management.ts b/x-pack/test/accessibility/apps/index_lifecycle_management.ts index 35f4a8e1adea57..6cec8d1cb891ab 100644 --- a/x-pack/test/accessibility/apps/index_lifecycle_management.ts +++ b/x-pack/test/accessibility/apps/index_lifecycle_management.ts @@ -7,6 +7,7 @@ import { FtrProviderContext } from '../ftr_provider_context'; +const REPO_NAME = 'test'; const POLICY_NAME = 'ilm-a11y-test'; const POLICY_ALL_PHASES = { policy: { @@ -23,7 +24,7 @@ const POLICY_ALL_PHASES = { frozen: { actions: { searchable_snapshot: { - snapshot_repository: 'test', + snapshot_repository: REPO_NAME, }, }, }, @@ -46,7 +47,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esClient = getService('es'); const a11y = getService('a11y'); + const filterByPolicyName = async (policyName: string) => { + await testSubjects.setValue('ilmSearchBar', policyName); + }; + const findPolicyLinkInListView = async (policyName: string) => { + await filterByPolicyName(policyName); const links = await testSubjects.findAll('policyTablePolicyNameLink'); for (const link of links) { const name = await link.getVisibleText(); @@ -57,11 +63,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { throw new Error(`Could not find ${policyName} in policy table`); }; - // FLAKY - // https://github.com/elastic/kibana/issues/114541 - // https://github.com/elastic/kibana/issues/114542 - describe.skip('Index Lifecycle Management', async () => { + describe('Index Lifecycle Management', async () => { before(async () => { + await esClient.snapshot.createRepository({ + name: REPO_NAME, + body: { + type: 'fs', + settings: { + // use one of the values defined in path.repo in test/functional/config.js + location: '/tmp/', + }, + }, + verify: false, + }); await esClient.ilm.putLifecycle({ name: POLICY_NAME, body: POLICY_ALL_PHASES }); await esClient.indices.putIndexTemplate({ name: indexTemplateName, @@ -79,6 +93,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); after(async () => { + await esClient.snapshot.deleteRepository({ + name: REPO_NAME, + }); await esClient.ilm.deleteLifecycle({ name: POLICY_NAME }); await esClient.indices.deleteIndexTemplate({ name: indexTemplateName }); }); @@ -144,6 +161,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('Add policy to index template modal', async () => { + await filterByPolicyName(POLICY_NAME); const policyRow = await testSubjects.find(`policyTableRow-${POLICY_NAME}`); const addPolicyButton = await policyRow.findByTestSubject('addPolicyToTemplate'); @@ -157,6 +175,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('Delete policy modal', async () => { + await filterByPolicyName(POLICY_NAME); const policyRow = await testSubjects.find(`policyTableRow-${POLICY_NAME}`); const deleteButton = await policyRow.findByTestSubject('deletePolicy'); @@ -170,6 +189,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('Index templates flyout', async () => { + await filterByPolicyName(POLICY_NAME); const policyRow = await testSubjects.find(`policyTableRow-${POLICY_NAME}`); const actionsButton = await policyRow.findByTestSubject('viewIndexTemplates'); diff --git a/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts b/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts index f7510c3c303184..95ddd0a7b59448 100644 --- a/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts +++ b/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts @@ -9,6 +9,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; const policyName = 'testPolicy1'; +const repoName = 'test'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common', 'indexLifecycleManagement']); @@ -16,12 +17,23 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const retry = getService('retry'); const esClient = getService('es'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/114473 and https://github.com/elastic/kibana/issues/114474 - describe.skip('Home page', function () { + describe('Home page', function () { before(async () => { + await esClient.snapshot.createRepository({ + name: repoName, + body: { + type: 'fs', + settings: { + // use one of the values defined in path.repo in test/functional/config.js + location: '/tmp/', + }, + }, + verify: false, + }); await pageObjects.common.navigateToApp('indexLifecycleManagement'); }); after(async () => { + await esClient.snapshot.deleteRepository({ name: repoName }); await esClient.ilm.deleteLifecycle({ name: policyName }); }); @@ -41,6 +53,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { coldEnabled: true, frozenEnabled: true, deleteEnabled: true, + snapshotRepository: repoName, }); await retry.waitFor('navigation back to home page.', async () => { From 753da1ac1dc042a507deb3b9daec87d22b9e5a74 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Thu, 28 Oct 2021 13:55:20 +0200 Subject: [PATCH 45/46] [ML] Functional tests - remove monitor cluster privilege from test role (#116581) This PR removes the `monitor` cluster privilege from the `ft_ml_ui_extras` test role as it's no longer required by the categorization wizard and we want to stay close to the minimum set of required privileges for our test users. --- x-pack/test/functional/services/ml/security_common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/services/ml/security_common.ts b/x-pack/test/functional/services/ml/security_common.ts index 54d2fa48a826f0..925565143bda0b 100644 --- a/x-pack/test/functional/services/ml/security_common.ts +++ b/x-pack/test/functional/services/ml/security_common.ts @@ -58,7 +58,7 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide { name: 'ft_ml_ui_extras', elasticsearch: { - cluster: ['manage_ingest_pipelines', 'monitor'], + cluster: ['manage_ingest_pipelines'], }, kibana: [], }, From 0f73b57cfd0a723b8d1ad137595f585c6f3d1d97 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Thu, 28 Oct 2021 14:18:53 +0200 Subject: [PATCH 46/46] [ML] Anomaly Detection: Functional tests for anomaly detection forecasts. (#116140) Functional tests for anomaly detection forecasts. --- .../forecasting_modal/forecasts_list.js | 7 + .../timeseries_chart/timeseries_chart.js | 15 ++- .../timeseriesexplorer/timeseriesexplorer.js | 10 +- .../apps/ml/anomaly_detection/forecasts.ts | 116 ++++++++++++++++ .../apps/ml/anomaly_detection/index.ts | 1 + .../apps/ml/permissions/full_ml_access.ts | 10 +- .../apps/ml/permissions/read_ml_access.ts | 10 +- .../test/functional/services/ml/forecast.ts | 126 ++++++++++++++++++ x-pack/test/functional/services/ml/index.ts | 3 + .../services/ml/single_metric_viewer.ts | 40 ------ 10 files changed, 282 insertions(+), 56 deletions(-) create mode 100644 x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts create mode 100644 x-pack/test/functional/services/ml/forecast.ts diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js index 1854982c8db0bf..7f9fcc7bc55170 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js @@ -78,6 +78,12 @@ function getColumns(viewForecast) { // TODO - add in ml-info-icon to the h3 element, // then remove tooltip and inline style. export function ForecastsList({ forecasts, viewForecast }) { + const getRowProps = (item) => { + return { + 'data-test-subj': `mlForecastsListRow row-${item.rowId}`, + }; + }; + return (

); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index 87131583e44eb4..cad5bb68fb62b7 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -547,9 +547,18 @@ class TimeseriesChartIntl extends Component { // Create the path elements for the forecast value line and bounds area. if (contextForecastData) { - fcsGroup.append('path').attr('class', 'area forecast'); - fcsGroup.append('path').attr('class', 'values-line forecast'); - fcsGroup.append('g').attr('class', 'focus-chart-markers forecast'); + fcsGroup + .append('path') + .attr('class', 'area forecast') + .attr('data-test-subj', 'mlForecastArea'); + fcsGroup + .append('path') + .attr('class', 'values-line forecast') + .attr('data-test-subj', 'mlForecastValuesline'); + fcsGroup + .append('g') + .attr('class', 'focus-chart-markers forecast') + .attr('data-test-subj', 'mlForecastMarkers'); } fcsGroup diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index 9b8770350909ea..e4d7fc457de0b7 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -1170,9 +1170,13 @@ export class TimeSeriesExplorer extends React.Component { + {i18n.translate('xpack.ml.timeSeriesExplorer.showForecastLabel', { + defaultMessage: 'show forecast', + })} + + } checked={showForecast} onChange={this.toggleShowForecastHandler} /> diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts b/x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts new file mode 100644 index 00000000000000..f65653e2c03c5c --- /dev/null +++ b/x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { Job, Datafeed } from '../../../../../plugins/ml/common/types/anomaly_detection_jobs'; + +// @ts-expect-error not full interface +const JOB_CONFIG: Job = { + job_id: `fq_single_1_smv`, + description: 'count() on farequote dataset with 15m bucket span', + groups: ['farequote', 'automated', 'single-metric'], + analysis_config: { + bucket_span: '15m', + influencers: [], + detectors: [ + { + function: 'count', + }, + ], + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '10mb' }, + model_plot_config: { enabled: true }, +}; + +// @ts-expect-error not full interface +const DATAFEED_CONFIG: Datafeed = { + datafeed_id: 'datafeed-fq_single_1_smv', + indices: ['ft_farequote'], + job_id: 'fq_single_1_smv', + query: { bool: { must: [{ match_all: {} }] } }, +}; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('forecasts', function () { + this.tags(['mlqa']); + + describe('with single metric job', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); + await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.setKibanaTimeZoneToUTC(); + + await ml.api.createAndRunAnomalyDetectionLookbackJob(JOB_CONFIG, DATAFEED_CONFIG); + await ml.securityUI.loginAsMlPowerUser(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + it('opens a job from job list link', async () => { + await ml.testExecution.logTestStep('navigate to job list'); + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToJobManagement(); + + await ml.testExecution.logTestStep('open job in single metric viewer'); + await ml.jobTable.waitForJobsToLoad(); + await ml.jobTable.filterWithSearchString(JOB_CONFIG.job_id, 1); + + await ml.jobTable.clickOpenJobInSingleMetricViewerButton(JOB_CONFIG.job_id); + await ml.commonUI.waitForMlLoadingIndicatorToDisappear(); + }); + + it('displays job results', async () => { + await ml.testExecution.logTestStep('pre-fills the job selection'); + await ml.jobSelection.assertJobSelection([JOB_CONFIG.job_id]); + + await ml.testExecution.logTestStep('pre-fills the detector input'); + await ml.singleMetricViewer.assertDetectorInputExist(); + await ml.singleMetricViewer.assertDetectorInputValue('0'); + + await ml.testExecution.logTestStep('displays the chart'); + await ml.singleMetricViewer.assertChartExist(); + + await ml.testExecution.logTestStep('should not display the forecasts toggle checkbox'); + await ml.forecast.assertForecastCheckboxMissing(); + + await ml.testExecution.logTestStep('should open the forecasts modal'); + await ml.forecast.assertForecastButtonExists(); + await ml.forecast.assertForecastButtonEnabled(true); + await ml.forecast.openForecastModal(); + await ml.forecast.assertForecastModalRunButtonEnabled(true); + + await ml.testExecution.logTestStep('should run the forecast and close the modal'); + await ml.forecast.clickForecastModalRunButton(); + + await ml.testExecution.logTestStep('should display the forecasts toggle checkbox'); + await ml.forecast.assertForecastCheckboxExists(); + + await ml.testExecution.logTestStep( + 'should display the forecast in the single metric chart' + ); + await ml.forecast.assertForecastChartElementsExists(); + + await ml.testExecution.logTestStep('should hide the forecast in the single metric chart'); + await ml.forecast.clickForecastCheckbox(); + await ml.forecast.assertForecastChartElementsHidden(); + + await ml.testExecution.logTestStep('should open the forecasts modal and list the forecast'); + await ml.forecast.assertForecastButtonExists(); + await ml.forecast.assertForecastButtonEnabled(true); + await ml.forecast.openForecastModal(); + await ml.forecast.assertForecastTableExists(); + await ml.forecast.assertForecastTableNotEmpty(); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/index.ts b/x-pack/test/functional/apps/ml/anomaly_detection/index.ts index d87da8469db118..ed5f618f86644c 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/index.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/index.ts @@ -24,5 +24,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./annotations')); loadTestFile(require.resolve('./aggregated_scripted_job')); loadTestFile(require.resolve('./custom_urls')); + loadTestFile(require.resolve('./forecasts')); }); } diff --git a/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts index 448774f1a0c7f1..356e3822179647 100644 --- a/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts @@ -237,11 +237,11 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( 'should display the forecast modal with enabled run button' ); - await ml.singleMetricViewer.assertForecastButtonExists(); - await ml.singleMetricViewer.assertForecastButtonEnabled(true); - await ml.singleMetricViewer.openForecastModal(); - await ml.singleMetricViewer.assertForecastModalRunButtonEnabled(true); - await ml.singleMetricViewer.closeForecastModal(); + await ml.forecast.assertForecastButtonExists(); + await ml.forecast.assertForecastButtonEnabled(true); + await ml.forecast.openForecastModal(); + await ml.forecast.assertForecastModalRunButtonEnabled(true); + await ml.forecast.closeForecastModal(); }); it('should display elements on Anomaly Explorer page correctly', async () => { diff --git a/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts index b96da748507866..be57904b944514 100644 --- a/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts @@ -230,11 +230,11 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( 'should display the forecast modal with disabled run button' ); - await ml.singleMetricViewer.assertForecastButtonExists(); - await ml.singleMetricViewer.assertForecastButtonEnabled(true); - await ml.singleMetricViewer.openForecastModal(); - await ml.singleMetricViewer.assertForecastModalRunButtonEnabled(false); - await ml.singleMetricViewer.closeForecastModal(); + await ml.forecast.assertForecastButtonExists(); + await ml.forecast.assertForecastButtonEnabled(true); + await ml.forecast.openForecastModal(); + await ml.forecast.assertForecastModalRunButtonEnabled(false); + await ml.forecast.closeForecastModal(); }); it('should display elements on Anomaly Explorer page correctly', async () => { diff --git a/x-pack/test/functional/services/ml/forecast.ts b/x-pack/test/functional/services/ml/forecast.ts new file mode 100644 index 00000000000000..c26216c97adfe3 --- /dev/null +++ b/x-pack/test/functional/services/ml/forecast.ts @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function MachineLearningForecastProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertForecastButtonExists() { + await testSubjects.existOrFail( + 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' + ); + }, + + async assertForecastButtonEnabled(expectedValue: boolean) { + const isEnabled = await testSubjects.isEnabled( + 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' + ); + expect(isEnabled).to.eql( + expectedValue, + `Expected "forecast" button to be '${expectedValue ? 'enabled' : 'disabled'}' (got '${ + isEnabled ? 'enabled' : 'disabled' + }')` + ); + }, + + async assertForecastChartElementsExists() { + await testSubjects.existOrFail(`mlForecastArea`, { + timeout: 30 * 1000, + }); + await testSubjects.existOrFail(`mlForecastValuesline`, { + timeout: 30 * 1000, + }); + await testSubjects.existOrFail(`mlForecastMarkers`, { + timeout: 30 * 1000, + }); + }, + + async assertForecastChartElementsHidden() { + await testSubjects.missingOrFail(`mlForecastArea`, { + allowHidden: true, + timeout: 30 * 1000, + }); + await testSubjects.missingOrFail(`mlForecastValuesline`, { + allowHidden: true, + timeout: 30 * 1000, + }); + await testSubjects.missingOrFail(`mlForecastMarkers`, { + allowHidden: true, + timeout: 30 * 1000, + }); + }, + + async assertForecastCheckboxExists() { + await testSubjects.existOrFail(`mlForecastCheckbox`, { + timeout: 30 * 1000, + }); + }, + + async assertForecastCheckboxMissing() { + await testSubjects.missingOrFail(`mlForecastCheckbox`, { + timeout: 30 * 1000, + }); + }, + + async clickForecastCheckbox() { + await testSubjects.click('mlForecastCheckbox'); + }, + + async openForecastModal() { + await testSubjects.click( + 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' + ); + await testSubjects.existOrFail('mlModalForecast'); + }, + + async closeForecastModal() { + await testSubjects.click('mlModalForecast > mlModalForecastButtonClose'); + await this.assertForecastModalMissing(); + }, + + async assertForecastModalMissing() { + await testSubjects.missingOrFail(`mlModalForecast`, { + timeout: 30 * 1000, + }); + }, + + async assertForecastModalRunButtonEnabled(expectedValue: boolean) { + const isEnabled = await testSubjects.isEnabled('mlModalForecast > mlModalForecastButtonRun'); + expect(isEnabled).to.eql( + expectedValue, + `Expected forecast "run" button to be '${expectedValue ? 'enabled' : 'disabled'}' (got '${ + isEnabled ? 'enabled' : 'disabled' + }')` + ); + }, + + async assertForecastTableExists() { + await testSubjects.existOrFail('mlModalForecast > mlModalForecastTable'); + }, + + async clickForecastModalRunButton() { + await testSubjects.click('mlModalForecast > mlModalForecastButtonRun'); + await this.assertForecastModalMissing(); + }, + + async getForecastTableRows() { + return await testSubjects.findAll('mlModalForecastTable > ~mlForecastsListRow'); + }, + + async assertForecastTableNotEmpty() { + const tableRows = await this.getForecastTableRows(); + expect(tableRows.length).to.be.greaterThan( + 0, + `Forecast table should have at least one row (got '${tableRows.length}')` + ); + }, + }; +} diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index 17302b27822237..4b48e4c0269eb9 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -24,6 +24,7 @@ import { MachineLearningDataVisualizerProvider } from './data_visualizer'; import { MachineLearningDataVisualizerFileBasedProvider } from './data_visualizer_file_based'; import { MachineLearningDataVisualizerIndexBasedProvider } from './data_visualizer_index_based'; import { MachineLearningDataVisualizerIndexPatternManagementProvider } from './data_visualizer_index_pattern_management'; +import { MachineLearningForecastProvider } from './forecast'; import { MachineLearningJobManagementProvider } from './job_management'; import { MachineLearningJobSelectionProvider } from './job_selection'; import { MachineLearningJobSourceSelectionProvider } from './job_source_selection'; @@ -92,6 +93,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { const dataVisualizerIndexPatternManagement = MachineLearningDataVisualizerIndexPatternManagementProvider(context, dataVisualizerTable); + const forecast = MachineLearningForecastProvider(context); const jobAnnotations = MachineLearningJobAnnotationsProvider(context); const jobManagement = MachineLearningJobManagementProvider(context, api); const jobSelection = MachineLearningJobSelectionProvider(context); @@ -145,6 +147,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { dataVisualizerIndexBased, dataVisualizerIndexPatternManagement, dataVisualizerTable, + forecast, jobAnnotations, jobManagement, jobSelection, diff --git a/x-pack/test/functional/services/ml/single_metric_viewer.ts b/x-pack/test/functional/services/ml/single_metric_viewer.ts index ac3fd67e3f94e8..29f1ded74debaa 100644 --- a/x-pack/test/functional/services/ml/single_metric_viewer.ts +++ b/x-pack/test/functional/services/ml/single_metric_viewer.ts @@ -22,24 +22,6 @@ export function MachineLearningSingleMetricViewerProvider( await testSubjects.existOrFail('mlNoSingleMetricJobsFound'); }, - async assertForecastButtonExists() { - await testSubjects.existOrFail( - 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' - ); - }, - - async assertForecastButtonEnabled(expectedValue: boolean) { - const isEnabled = await testSubjects.isEnabled( - 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' - ); - expect(isEnabled).to.eql( - expectedValue, - `Expected "forecast" button to be '${expectedValue ? 'enabled' : 'disabled'}' (got '${ - isEnabled ? 'enabled' : 'disabled' - }')` - ); - }, - async assertDetectorInputExist() { await testSubjects.existOrFail( 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerDetectorSelect' @@ -97,28 +79,6 @@ export function MachineLearningSingleMetricViewerProvider( }); }, - async openForecastModal() { - await testSubjects.click( - 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' - ); - await testSubjects.existOrFail('mlModalForecast'); - }, - - async closeForecastModal() { - await testSubjects.click('mlModalForecast > mlModalForecastButtonClose'); - await testSubjects.missingOrFail('mlModalForecast'); - }, - - async assertForecastModalRunButtonEnabled(expectedValue: boolean) { - const isEnabled = await testSubjects.isEnabled('mlModalForecast > mlModalForecastButtonRun'); - expect(isEnabled).to.eql( - expectedValue, - `Expected forecast "run" button to be '${expectedValue ? 'enabled' : 'disabled'}' (got '${ - isEnabled ? 'enabled' : 'disabled' - }')` - ); - }, - async openAnomalyExplorer() { await testSubjects.click('mlAnomalyResultsViewSelectorExplorer'); await testSubjects.existOrFail('mlPageAnomalyExplorer');