From e08dc1689c3377e1f1623da7482ad03dfaac9e62 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 May 2024 10:11:19 -0600 Subject: [PATCH 1/4] [ML] decouple ML plugin from Map embeddable --- x-pack/plugins/maps/public/index.ts | 1 + .../new_job/common/map_loader/map_loader.ts | 5 ++--- .../components/geo_view/geo_map_examples.tsx | 21 ++++++++++++++----- .../components/geo_view/metric_selection.tsx | 8 +++---- .../geo_view/metric_selection_summary.tsx | 8 +++---- 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/maps/public/index.ts b/x-pack/plugins/maps/public/index.ts index d0281537e7a02d..4f8a1090436fe0 100644 --- a/x-pack/plugins/maps/public/index.ts +++ b/x-pack/plugins/maps/public/index.ts @@ -26,6 +26,7 @@ export type { } from './classes/tooltips/tooltip_property'; export type { MapsSetupApi, MapsStartApi } from './api'; +export type { CreateLayerDescriptorParams } from './classes/sources/es_search_source/create_layer_descriptor'; export type { MapEmbeddable, MapEmbeddableInput, MapEmbeddableOutput } from './embeddable'; export { type MapApi, isMapApi } from './embeddable/map_api'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/map_loader/map_loader.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/map_loader/map_loader.ts index aefedb6a11f22d..c45affe1861ad8 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/map_loader/map_loader.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/map_loader/map_loader.ts @@ -9,7 +9,7 @@ import memoizeOne from 'memoize-one'; import { isEqual } from 'lodash'; import type { DataView } from '@kbn/data-views-plugin/common'; import type { ES_GEO_FIELD_TYPE, LayerDescriptor } from '@kbn/maps-plugin/common'; -import type { MapsStartApi } from '@kbn/maps-plugin/public'; +import type { CreateLayerDescriptorParams, MapsStartApi } from '@kbn/maps-plugin/public'; import type { Query } from '@kbn/es-query'; import type { Field, SplitField } from '@kbn/ml-anomaly-utils'; import { ChartLoader } from '../chart_loader'; @@ -41,11 +41,10 @@ export class MapLoader extends ChartLoader { ? `${splitField.name}:${fieldValues[0]} ${query ? `and ${query}` : ''}` : `${query ? query : ''}`; - const params: any = { + const params: CreateLayerDescriptorParams = { indexPatternId: this._dataView.id, geoFieldName: geoField.name, geoFieldType: geoField.type as unknown as ES_GEO_FIELD_TYPE, - filters: filters ?? [], query: { query: queryString, language: 'kuery' }, }; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/geo_map_examples.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/geo_map_examples.tsx index 4e2722e46e41b7..efef4ef68aec62 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/geo_map_examples.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/geo_map_examples.tsx @@ -8,10 +8,10 @@ import type { FC } from 'react'; import React from 'react'; import { EuiFlexGrid, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import type { LayerDescriptor } from '@kbn/maps-plugin/common'; +import { INITIAL_LOCATION, LayerDescriptor } from '@kbn/maps-plugin/common'; import type { Aggregation, Field, SplitField } from '@kbn/ml-anomaly-utils'; import { SplitCards, useAnimateSplit } from '../split_cards'; -import { MlEmbeddedMapComponent } from '../../../../../../../components/ml_embedded_map'; +import { useMlKibana } from '../../../../../../../contexts/kibana'; import { JOB_TYPE } from '../../../../../../../../../common/constants/new_job'; import { DetectorTitle } from '../detector_title'; @@ -31,6 +31,10 @@ export const GeoMapExamples: FC = ({ geoAgg, layerList, }) => { + const { + services: { maps: mapsPlugin }, + } = useMlKibana(); + const animateSplit = useAnimateSplit(); return ( @@ -46,9 +50,16 @@ export const GeoMapExamples: FC = ({ <> {geoAgg && geoField ? : null} - - - + {mapsPlugin && + + } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection.tsx index 1938eefc99611b..58df31f9423e19 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection.tsx @@ -29,7 +29,7 @@ export const GeoDetector: FC = ({ setIsValid }) => { const [layerList, setLayerList] = useState([]); const { - services: { data, notifications: toasts }, + services: { notifications: toasts }, } = useMlKibana(); const { mapLoader } = useContext(JobCreatorContext); @@ -72,14 +72,12 @@ export const GeoDetector: FC = ({ setIsValid }) => { useEffect(() => { async function getMapLayersForGeoJob() { if (jobCreator.geoField) { - const { filter, query } = jobCreator.savedSearchQuery ?? {}; - const filters = [...data.query.filterManager.getFilters(), ...(filter ?? [])]; - + const { query } = jobCreator.savedSearchQuery ?? {}; + const layers = await mapLoader.getMapLayersForGeoJob( jobCreator.geoField, jobCreator.splitField, fieldValues, - filters, query ); setLayerList(layers); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection_summary.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection_summary.tsx index 7845a62496d8cc..1e8ed527416579 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection_summary.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection_summary.tsx @@ -24,7 +24,7 @@ export const GeoDetectorsSummary: FC = () => { const splitField = jobCreator.splitField; const { - services: { data, notifications }, + services: { notifications }, } = useMlKibana(); // Load example field values for summary view @@ -56,14 +56,12 @@ export const GeoDetectorsSummary: FC = () => { useEffect(() => { async function getMapLayersForGeoJob() { if (geoField) { - const { filter, query } = jobCreator.savedSearchQuery ?? {}; - const filters = [...data.query.filterManager.getFilters(), ...(filter ?? [])]; - + const { query } = jobCreator.savedSearchQuery ?? {}; + const layers = await mapLoader.getMapLayersForGeoJob( geoField, splitField, fieldValues, - filters, query ); setLayerList(layers); From b9f1cc488ad0b97f276e7938d0f561694ba31f8e Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 May 2024 10:50:56 -0600 Subject: [PATCH 2/4] remove EmbeddedMap --- .../components/ml_embedded_map/index.ts | 8 - .../ml_embedded_map/ml_embedded_map.tsx | 154 ------------------ .../application/explorer/anomalies_map.tsx | 13 +- .../explorer_chart_embedded_map.tsx | 20 ++- .../components/geo_view/geo_map_examples.tsx | 25 +-- .../components/geo_view/metric_selection.tsx | 2 +- .../geo_view/metric_selection_summary.tsx | 2 +- 7 files changed, 43 insertions(+), 181 deletions(-) delete mode 100644 x-pack/plugins/ml/public/application/components/ml_embedded_map/index.ts delete mode 100644 x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx diff --git a/x-pack/plugins/ml/public/application/components/ml_embedded_map/index.ts b/x-pack/plugins/ml/public/application/components/ml_embedded_map/index.ts deleted file mode 100644 index 3829e98450d739..00000000000000 --- a/x-pack/plugins/ml/public/application/components/ml_embedded_map/index.ts +++ /dev/null @@ -1,8 +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 { MlEmbeddedMapComponent } from './ml_embedded_map'; diff --git a/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx b/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx deleted file mode 100644 index 02a713af1b7ae5..00000000000000 --- a/x-pack/plugins/ml/public/application/components/ml_embedded_map/ml_embedded_map.tsx +++ /dev/null @@ -1,154 +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, { useEffect, useRef, useState } from 'react'; - -import { htmlIdGenerator } from '@elastic/eui'; -import type { LayerDescriptor } from '@kbn/maps-plugin/common'; -import { INITIAL_LOCATION, MAP_SAVED_OBJECT_TYPE } from '@kbn/maps-plugin/common'; -import type { - MapEmbeddable, - MapEmbeddableInput, - MapEmbeddableOutput, - RenderTooltipContentParams, -} from '@kbn/maps-plugin/public'; - -import type { EmbeddableFactory, ErrorEmbeddable } from '@kbn/embeddable-plugin/public'; -import { isErrorEmbeddable, ViewMode } from '@kbn/embeddable-plugin/public'; -import { useMlKibana } from '../../contexts/kibana'; - -export function MlEmbeddedMapComponent({ - layerList, - mapEmbeddableInput, - renderTooltipContent, -}: { - layerList: LayerDescriptor[]; - mapEmbeddableInput?: MapEmbeddableInput; - renderTooltipContent?: (params: RenderTooltipContentParams) => JSX.Element; -}) { - const [embeddable, setEmbeddable] = useState(); - - const embeddableRoot: React.RefObject = useRef(null); - const baseLayers = useRef(); - - const { - services: { embeddable: embeddablePlugin, maps: mapsPlugin }, - } = useMlKibana(); - - const factory: - | EmbeddableFactory - | undefined = embeddablePlugin - ? embeddablePlugin.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE) - : undefined; - - // Update the layer list with updated geo points upon refresh - useEffect(() => { - async function updateIndexPatternSearchLayer() { - if ( - embeddable && - !isErrorEmbeddable(embeddable) && - Array.isArray(layerList) && - Array.isArray(baseLayers.current) - ) { - embeddable.setLayerList([...baseLayers.current, ...layerList]); - } - } - updateIndexPatternSearchLayer(); - }, [embeddable, layerList]); - - useEffect(() => { - async function setupEmbeddable() { - if (!factory) { - // eslint-disable-next-line no-console - console.error('Map embeddable not found.'); - return; - } - const input: MapEmbeddableInput = { - id: htmlIdGenerator()(), - attributes: { title: '' }, - filters: [], - hidePanelTitles: true, - viewMode: ViewMode.VIEW, - isLayerTOCOpen: false, - hideFilterActions: true, - // can use mapSettings to center map on anomalies - mapSettings: { - disableInteractive: false, - hideToolbarOverlay: false, - hideLayerControl: false, - hideViewControl: false, - initialLocation: INITIAL_LOCATION.AUTO_FIT_TO_BOUNDS, // this will startup based on data-extent - autoFitToDataBounds: true, // this will auto-fit when there are changes to the filter and/or query - }, - }; - - const embeddableObject = await factory.create(input); - - if (embeddableObject && !isErrorEmbeddable(embeddableObject)) { - const basemapLayerDescriptor = mapsPlugin - ? await mapsPlugin.createLayerDescriptors.createBasemapLayerDescriptor() - : null; - - if (basemapLayerDescriptor) { - baseLayers.current = [basemapLayerDescriptor]; - await embeddableObject.setLayerList(baseLayers.current); - } - } - - setEmbeddable(embeddableObject); - } - - setupEmbeddable(); - // we want this effect to execute exactly once after the component mounts - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - if (embeddable && !isErrorEmbeddable(embeddable) && mapEmbeddableInput !== undefined) { - embeddable.updateInput(mapEmbeddableInput); - } - }, [embeddable, mapEmbeddableInput]); - - useEffect(() => { - if (embeddable && !isErrorEmbeddable(embeddable) && renderTooltipContent !== undefined) { - embeddable.setRenderTooltipContent(renderTooltipContent); - } - }, [embeddable, renderTooltipContent]); - - // We can only render after embeddable has already initialized - useEffect(() => { - if (embeddableRoot.current && embeddable) { - embeddable.render(embeddableRoot.current); - } - }, [embeddable, embeddableRoot]); - - if (!embeddablePlugin) { - // eslint-disable-next-line no-console - console.error('Embeddable start plugin not found'); - return null; - } - if (!mapsPlugin) { - // eslint-disable-next-line no-console - console.error('Maps start plugin not found'); - return null; - } - - return ( -
- ); -} diff --git a/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx b/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx index 60dbe1f6e845f2..74da9e66ca6fad 100644 --- a/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx +++ b/x-pack/plugins/ml/public/application/explorer/anomalies_map.tsx @@ -18,6 +18,7 @@ import { htmlIdGenerator, } from '@elastic/eui'; import type { VectorLayerDescriptor } from '@kbn/maps-plugin/common'; +import { INITIAL_LOCATION } from '@kbn/maps-plugin/common'; import { FIELD_ORIGIN, LAYER_TYPE, @@ -29,7 +30,6 @@ import type { EMSTermJoinConfig } from '@kbn/maps-plugin/public'; import { isDefined } from '@kbn/ml-is-defined'; import type { MlAnomaliesTableRecord } from '@kbn/ml-anomaly-utils'; import { useMlKibana } from '../contexts/kibana'; -import { MlEmbeddedMapComponent } from '../components/ml_embedded_map'; const MAX_ENTITY_VALUES = 3; @@ -253,7 +253,16 @@ export const AnomaliesMap: FC = ({ anomalies, jobIds }) => { data-test-subj="mlAnomalyExplorerAnomaliesMap" style={{ width: '100%', height: 300 }} > - + {mapsPlugin && ( + + )}
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_embedded_map.tsx b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_embedded_map.tsx index 5d2946358eb74f..ac8b080fb8d8c3 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_embedded_map.tsx +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_embedded_map.tsx @@ -7,14 +7,19 @@ import React, { useState, useEffect } from 'react'; import type { LayerDescriptor } from '@kbn/maps-plugin/common'; +import { INITIAL_LOCATION } from '@kbn/maps-plugin/common'; import type { Dictionary } from '../../../../common/types/common'; import { getMLAnomaliesActualLayer, getMLAnomaliesTypicalLayer } from './map_config'; -import { MlEmbeddedMapComponent } from '../../components/ml_embedded_map'; +import { useMlKibana } from '../../contexts/kibana'; interface Props { seriesConfig: Dictionary; } export function EmbeddedMapComponentWrapper({ seriesConfig }: Props) { + const { + services: { maps: mapsPlugin }, + } = useMlKibana(); + const [layerList, setLayerList] = useState([]); useEffect(() => { @@ -26,9 +31,16 @@ export function EmbeddedMapComponentWrapper({ seriesConfig }: Props) { } }, [seriesConfig]); - return ( + return mapsPlugin ? (
- +
- ); + ) : null; } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/geo_map_examples.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/geo_map_examples.tsx index efef4ef68aec62..da95646665ffc6 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/geo_map_examples.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/geo_map_examples.tsx @@ -8,7 +8,8 @@ import type { FC } from 'react'; import React from 'react'; import { EuiFlexGrid, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { INITIAL_LOCATION, LayerDescriptor } from '@kbn/maps-plugin/common'; +import type { LayerDescriptor } from '@kbn/maps-plugin/common'; +import { INITIAL_LOCATION } from '@kbn/maps-plugin/common'; import type { Aggregation, Field, SplitField } from '@kbn/ml-anomaly-utils'; import { SplitCards, useAnimateSplit } from '../split_cards'; import { useMlKibana } from '../../../../../../../contexts/kibana'; @@ -50,16 +51,18 @@ export const GeoMapExamples: FC = ({ <> {geoAgg && geoField ? : null} - {mapsPlugin && - - } + {mapsPlugin && ( + + + + )} diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection.tsx index 58df31f9423e19..9d8a6edabe583b 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection.tsx @@ -73,7 +73,7 @@ export const GeoDetector: FC = ({ setIsValid }) => { async function getMapLayersForGeoJob() { if (jobCreator.geoField) { const { query } = jobCreator.savedSearchQuery ?? {}; - + const layers = await mapLoader.getMapLayersForGeoJob( jobCreator.geoField, jobCreator.splitField, diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection_summary.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection_summary.tsx index 1e8ed527416579..58ff2598973811 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection_summary.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/geo_view/metric_selection_summary.tsx @@ -57,7 +57,7 @@ export const GeoDetectorsSummary: FC = () => { async function getMapLayersForGeoJob() { if (geoField) { const { query } = jobCreator.savedSearchQuery ?? {}; - + const layers = await mapLoader.getMapLayersForGeoJob( geoField, splitField, From d0ff484844e3f127b40aab90c9dda2e1f647169e Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 May 2024 10:54:00 -0600 Subject: [PATCH 3/4] tslint --- .../application/jobs/new_job/common/map_loader/map_loader.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/map_loader/map_loader.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/map_loader/map_loader.ts index c45affe1861ad8..242ce9d4724952 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/map_loader/map_loader.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/map_loader/map_loader.ts @@ -30,7 +30,6 @@ export class MapLoader extends ChartLoader { geoField: Field, splitField: SplitField, fieldValues: string[], - filters?: any[], savedSearchQuery?: Query ) { const layerList: LayerDescriptor[] = []; From a65f88e919e63c02bf32fa297e15b54108695921 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 2 May 2024 11:55:52 -0600 Subject: [PATCH 4/4] update expects --- x-pack/test/functional/services/ml/job_wizard_geo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/services/ml/job_wizard_geo.ts b/x-pack/test/functional/services/ml/job_wizard_geo.ts index 39202125669323..be2848985a6a26 100644 --- a/x-pack/test/functional/services/ml/job_wizard_geo.ts +++ b/x-pack/test/functional/services/ml/job_wizard_geo.ts @@ -46,7 +46,7 @@ export function MachineLearningJobWizardGeoProvider({ getService }: FtrProviderC ); await testSubjects.existOrFail('mlGeoJobWizardMap'); - await testSubjects.existOrFail('mlEmbeddedMapContent'); + await testSubjects.existOrFail('mapContainer'); }, }; }