From a908ef8cad4c0a3fcd5b2e9591861d493fee21ba Mon Sep 17 00:00:00 2001 From: Junqiu Lei Date: Tue, 20 Dec 2022 10:01:18 -0800 Subject: [PATCH] Add global query bar Signed-off-by: Junqiu Lei --- .../document_layer_config_panel.tsx | 2 + .../document_layer_source.tsx | 12 +- .../layer_config/layer_config_panel.tsx | 7 + .../layer_control_panel.tsx | 565 +++++++++--------- .../map_container/map_container.scss | 6 +- .../map_container/map_container.tsx | 22 +- .../public/components/map_page/map_page.tsx | 36 +- .../components/map_top_nav/top_nav_menu.tsx | 31 +- .../public/model/DataLayerController.ts | 67 +++ maps_dashboards/public/model/mapLayerType.ts | 2 +- 10 files changed, 439 insertions(+), 311 deletions(-) create mode 100644 maps_dashboards/public/model/DataLayerController.ts diff --git a/maps_dashboards/public/components/layer_config/documents_config/document_layer_config_panel.tsx b/maps_dashboards/public/components/layer_config/documents_config/document_layer_config_panel.tsx index eee8f446..1a2589bc 100644 --- a/maps_dashboards/public/components/layer_config/documents_config/document_layer_config_panel.tsx +++ b/maps_dashboards/public/components/layer_config/documents_config/document_layer_config_panel.tsx @@ -5,6 +5,7 @@ import React, { Fragment } from 'react'; import { EuiSpacer, EuiTabbedContent } from '@elastic/eui'; +import { IndexPattern } from '../../../../../../src/plugins/data/public'; import { DocumentLayerSpecification } from '../../../model/mapLayerType'; import { LayerBasicSettings } from '../layer_basic_settings'; import { DocumentLayerSource } from './document_layer_source'; @@ -14,6 +15,7 @@ interface Props { selectedLayerConfig: DocumentLayerSpecification; setSelectedLayerConfig: Function; setIsUpdateDisabled: Function; + layersIndexPatterns: IndexPattern[]; } export const DocumentLayerConfigPanel = (props: Props) => { diff --git a/maps_dashboards/public/components/layer_config/documents_config/document_layer_source.tsx b/maps_dashboards/public/components/layer_config/documents_config/document_layer_source.tsx index f87cdd5e..9b50d990 100644 --- a/maps_dashboards/public/components/layer_config/documents_config/document_layer_source.tsx +++ b/maps_dashboards/public/components/layer_config/documents_config/document_layer_source.tsx @@ -20,22 +20,24 @@ import { } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { FormattedMessage } from '@osd/i18n/react'; +import _, { Dictionary } from 'lodash'; import { IndexPattern, IndexPatternField } from '../../../../../../src/plugins/data/public'; import { useOpenSearchDashboards } from '../../../../../../src/plugins/opensearch_dashboards_react/public'; import { MapServices } from '../../../types'; import { DocumentLayerSpecification } from '../../../model/mapLayerType'; -import _, { Dictionary } from "lodash"; interface Props { setSelectedLayerConfig: Function; selectedLayerConfig: DocumentLayerSpecification; setIsUpdateDisabled: Function; + layersIndexPatterns: IndexPattern[]; } export const DocumentLayerSource = ({ setSelectedLayerConfig, selectedLayerConfig, setIsUpdateDisabled, + layersIndexPatterns, }: Props) => { const { services: { @@ -140,10 +142,10 @@ export const DocumentLayerSource = ({ useEffect(() => { const selectIndexPattern = async () => { if (selectedLayerConfig.source.indexPatternId) { - const savedIndexPattern = await indexPatterns.get( - selectedLayerConfig.source.indexPatternId + const selectedIndexPattern = layersIndexPatterns.find( + (ip) => ip.id === selectedLayerConfig.source.indexPatternId ); - setIndexPattern(savedIndexPattern); + setIndexPattern(selectedIndexPattern); } }; selectIndexPattern(); @@ -195,7 +197,7 @@ export const DocumentLayerSource = ({ const shouldTooltipSectionOpen = () => { return ( - selectedLayerConfig.source.showTooltips === true && + selectedLayerConfig.source.showTooltips && selectedLayerConfig.source.tooltipFields?.length > 0 ); }; diff --git a/maps_dashboards/public/components/layer_config/layer_config_panel.tsx b/maps_dashboards/public/components/layer_config/layer_config_panel.tsx index 0165728b..74ec3b21 100644 --- a/maps_dashboards/public/components/layer_config/layer_config_panel.tsx +++ b/maps_dashboards/public/components/layer_config/layer_config_panel.tsx @@ -26,6 +26,7 @@ import { MapLayerSpecification } from '../../model/mapLayerType'; import { BaseMapLayerConfigPanel } from './index'; import { DASHBOARDS_MAPS_LAYER_TYPE } from '../../../common'; import { DocumentLayerConfigPanel } from './documents_config/document_layer_config_panel'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; interface Props { closeLayerConfigPanel: Function; @@ -35,6 +36,8 @@ interface Props { removeLayer: Function; isNewLayer: boolean; setIsNewLayer: Function; + layersIndexPatterns: IndexPattern[]; + updateIndexPatterns: Function; } export const LayerConfigPanel = ({ @@ -45,6 +48,8 @@ export const LayerConfigPanel = ({ removeLayer, isNewLayer, setIsNewLayer, + layersIndexPatterns, + updateIndexPatterns, }: Props) => { const [isUpdateDisabled, setIsUpdateDisabled] = useState(false); const [originLayerConfig, setOriginLayerConfig] = useState(null); @@ -71,6 +76,7 @@ export const LayerConfigPanel = ({ }; const onUpdate = () => { updateLayer(); + updateIndexPatterns(); closeLayerConfigPanel(false); }; @@ -104,6 +110,7 @@ export const LayerConfigPanel = ({ selectedLayerConfig={selectedLayerConfig} setSelectedLayerConfig={setSelectedLayerConfig} setIsUpdateDisabled={setIsUpdateDisabled} + layersIndexPatterns={layersIndexPatterns} /> )} diff --git a/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx b/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx index a39c1296..2f065f76 100644 --- a/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx +++ b/maps_dashboards/public/components/layer_control_panel/layer_control_panel.tsx @@ -21,6 +21,7 @@ import { import { I18nProvider } from '@osd/i18n/react'; import { Map as Maplibre } from 'maplibre-gl'; import './layer_control_panel.scss'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; import { AddLayerPanel } from '../add_layer_panel'; import { LayerConfigPanel } from '../layer_config'; import { MapLayerSpecification } from '../../model/mapLayerType'; @@ -34,10 +35,7 @@ import { import { layersFunctionMap } from '../../model/layersFunctions'; import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public'; import { MapServices } from '../../types'; -import { - IOpenSearchDashboardsSearchResponse, - isCompleteResponse, -} from '../../../../../src/plugins/data/common'; +import { doDataLayerRender } from '../../model/DataLayerController'; interface MaplibreRef { current: Maplibre | null; @@ -47,318 +45,303 @@ interface Props { maplibreRef: MaplibreRef; setLayers: (layers: MapLayerSpecification[]) => void; layers: MapLayerSpecification[]; + layersIndexPatterns: IndexPattern[]; + setLayersIndexPatterns: (indexPatterns: IndexPattern[]) => void; } -const LayerControlPanel = memo(({ maplibreRef, setLayers, layers }: Props) => { - const { - services: { - data: { search }, - notifications, - }, - } = useOpenSearchDashboards(); +export const LayerControlPanel = memo( + ({ maplibreRef, setLayers, layers, layersIndexPatterns, setLayersIndexPatterns }: Props) => { + const { services } = useOpenSearchDashboards(); + const { + data: { indexPatterns }, + } = services; - const [isLayerConfigVisible, setIsLayerConfigVisible] = useState(false); - const [isLayerControlVisible, setIsLayerControlVisible] = useState(true); - const [selectedLayerConfig, setSelectedLayerConfig] = useState< - MapLayerSpecification | undefined - >(); - const [initialLayersLoaded, setInitialLayersLoaded] = useState(false); - const [addLayerId, setAddLayerId] = useState(''); - const [isUpdatingLayerRender, setIsUpdatingLayerRender] = useState(false); - const [isNewLayer, setIsNewLayer] = useState(false); + const [isLayerConfigVisible, setIsLayerConfigVisible] = useState(false); + const [isLayerControlVisible, setIsLayerControlVisible] = useState(true); + const [selectedLayerConfig, setSelectedLayerConfig] = useState< + MapLayerSpecification | undefined + >(); + const [initialLayersLoaded, setInitialLayersLoaded] = useState(false); + const [addLayerId, setAddLayerId] = useState(''); + const [isUpdatingLayerRender, setIsUpdatingLayerRender] = useState(false); + const [isNewLayer, setIsNewLayer] = useState(false); - useEffect(() => { - if (!isUpdatingLayerRender && initialLayersLoaded) { - return; - } - if (layers.length <= 0) { - return; - } - const doDataLayerRender = async (layer: MapLayerSpecification) => { - if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS) { - const sourceConfig = layer.source; - const indexPatternRefName = sourceConfig?.indexPatternRefName; - const geoField = sourceConfig.geoFieldName; - const sourceFields: string[] = [geoField]; - if (sourceConfig.showTooltips === true && sourceConfig.tooltipFields.length > 0) { - sourceFields.push(...sourceConfig.tooltipFields); + useEffect(() => { + if (!isUpdatingLayerRender && initialLayersLoaded) { + return; + } + if (layers.length <= 0) { + return; + } + + if (initialLayersLoaded) { + if (!selectedLayerConfig) { + return; } - const request = { - params: { - index: indexPatternRefName, - size: layer.source.documentRequestNumber, - body: { - _source: sourceFields, - }, - }, - }; - const search$ = search.search(request).subscribe({ - next: (response: IOpenSearchDashboardsSearchResponse) => { - if (isCompleteResponse(response)) { - const dataSource = response.rawResponse.hits.hits; - layersFunctionMap[layer.type].render(maplibreRef, layer, dataSource); - search$.unsubscribe(); - } else { - notifications.toasts.addWarning('An error has occurred when query dataSource'); - search$.unsubscribe(); - } - }, - error: (e: Error) => { - search.showError(e); - }, + if (selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { + layersFunctionMap[selectedLayerConfig.type].render(maplibreRef, selectedLayerConfig); + } else { + doDataLayerRender(selectedLayerConfig, services, layersIndexPatterns, maplibreRef); + } + if (addLayerId !== selectedLayerConfig.id) { + setSelectedLayerConfig(undefined); + } + } else { + layers.forEach((layer) => { + if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { + layersFunctionMap[layer.type].render(maplibreRef, layer); + } else { + doDataLayerRender(layer, services, layersIndexPatterns, maplibreRef); + } }); + setInitialLayersLoaded(true); } + setIsUpdatingLayerRender(false); + }, [layers]); + + const closeLayerConfigPanel = () => { + setIsLayerConfigVisible(false); + setTimeout(() => { + maplibreRef.current?.resize(); + }, 0); + }; + + const addLayer = (layer: MapLayerSpecification) => { + setLayers([...layers, layer]); + setAddLayerId(layer.id); }; - if (initialLayersLoaded) { + + const updateLayer = () => { if (!selectedLayerConfig) { return; } - if (selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { - layersFunctionMap[selectedLayerConfig.type].render(maplibreRef, selectedLayerConfig); + const layersClone = [...layers]; + const index = layersClone.findIndex((layer) => layer.id === selectedLayerConfig.id); + if (index <= -1) { + layersClone.push(selectedLayerConfig); } else { - doDataLayerRender(selectedLayerConfig); - } - if (addLayerId !== selectedLayerConfig.id) { - setSelectedLayerConfig(undefined); + layersClone[index] = { + ...layersClone[index], + ...selectedLayerConfig, + }; } - } else { - layers.forEach((layer) => { - if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { - layersFunctionMap[layer.type].render(maplibreRef, layer); - } else { - doDataLayerRender(layer); - } - }); - setInitialLayersLoaded(true); - } - setIsUpdatingLayerRender(false); - }, [layers]); - - const closeLayerConfigPanel = () => { - setIsLayerConfigVisible(false); - setTimeout(() => { - maplibreRef.current?.resize(); - }, 0); - }; - - const addLayer = (layer: MapLayerSpecification) => { - setLayers([...layers, layer]); - setAddLayerId(layer.id); - }; + setLayers(layersClone); + setIsUpdatingLayerRender(true); + }; - const updateLayer = () => { - if (!selectedLayerConfig) { - return; - } - const layersClone = [...layers]; - const index = layersClone.findIndex((layer) => layer.id === selectedLayerConfig.id); - if (index <= -1) { - layersClone.push(selectedLayerConfig); - } else { - layersClone[index] = { - ...layersClone[index], - ...selectedLayerConfig, - }; - } - setLayers(layersClone); - setIsUpdatingLayerRender(true); - }; + const removeLayer = (layerId: string) => { + const layersClone = [...layers]; + const index = layersClone.findIndex((layer) => layer.id === layerId); + if (index > -1) { + layersClone.splice(index, 1); + setLayers(layersClone); + } + }; - const removeLayer = (layerId: string) => { - const layersClone = [...layers]; - const index = layersClone.findIndex((layer) => layer.id === layerId); - if (index > -1) { - layersClone.splice(index, 1); - setLayers(layersClone); - } - }; + const onClickLayerName = (layer: MapLayerSpecification) => { + setSelectedLayerConfig(layer); + setIsLayerConfigVisible(true); + }; - const onClickLayerName = (layer: MapLayerSpecification) => { - setSelectedLayerConfig(layer); - setIsLayerConfigVisible(true); - }; + const [layerVisibility, setLayerVisibility] = useState(new Map([])); + layers.forEach((layer) => { + layerVisibility.set(layer.id, layer.visibility === LAYER_VISIBILITY.VISIBLE); + }); - const [layerVisibility, setLayerVisibility] = useState(new Map([])); - layers.forEach((layer) => { - layerVisibility.set(layer.id, layer.visibility === LAYER_VISIBILITY.VISIBLE); - }); + const onDragEnd = ({ source, destination }) => { + if (source && destination) { + const reorderedLayers = euiDragDropReorder(layers, source.index, destination.index); + setLayers(reorderedLayers); + // TODO: Refresh Maplibre layers + } + }; - const onDragEnd = ({ source, destination }) => { - if (source && destination) { - const reorderedLayers = euiDragDropReorder(layers, source.index, destination.index); - setLayers(reorderedLayers); - // TODO: Refresh Maplibre layers - } - }; + const getReverseLayers = () => { + const layersClone = [...layers]; + return layersClone.reverse(); + }; - const getReverseLayers = () => { - const layersClone = [...layers]; - return layersClone.reverse(); - }; + const updateIndexPatterns = async () => { + if (!selectedLayerConfig) { + return; + } + if (selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { + return; + } + const findIndexPattern = layersIndexPatterns.find( + (indexPattern) => indexPattern.id === selectedLayerConfig.source.indexPatternId + ); + if (!findIndexPattern) { + const newIndexPattern = await indexPatterns.get(selectedLayerConfig.source.indexPatternId); + const cloneLayersIndexPatterns = [...layersIndexPatterns, newIndexPattern]; + setLayersIndexPatterns(cloneLayersIndexPatterns); + } + }; - if (isLayerControlVisible) { - return ( - - - - - - -

Layer

-
-
- - setIsLayerControlVisible((visible) => !visible)} - aria-label="Hide layer control" - color="text" - className="layerControlPanel__visButton" - /> - -
- - - - {getReverseLayers().map((layer, index) => { - const isLayerSelected = - isLayerConfigVisible && - selectedLayerConfig && - selectedLayerConfig.id === layer.id; - return ( - - {(provided) => ( -
- - - onClickLayerName(layer)} - /> - - - - { - if (layer.visibility === LAYER_VISIBILITY.VISIBLE) { - layer.visibility = LAYER_VISIBILITY.NONE; - setLayerVisibility( - new Map(layerVisibility.set(layer.id, false)) - ); - } else { - layer.visibility = LAYER_VISIBILITY.VISIBLE; - setLayerVisibility( - new Map(layerVisibility.set(layer.id, true)) - ); - } - layersFunctionMap[layer.type]?.hide(maplibreRef, layer); - }} - aria-label="Hide or show layer" - color="text" - /> - - - { - layersFunctionMap[layer.type]?.remove(maplibreRef, layer); - removeLayer(layer.id); - }} - aria-label="Delete layer" - color="text" - /> - - - + + + + + +

Layer

+
+
+ + setIsLayerControlVisible((visible) => !visible)} + aria-label="Hide layer control" + color="text" + className="layerControlPanel__visButton" + /> + +
+ + + + {getReverseLayers().map((layer, index) => { + const isLayerSelected = + isLayerConfigVisible && + selectedLayerConfig && + selectedLayerConfig.id === layer.id; + return ( + + {(provided) => ( +
+ + + onClickLayerName(layer)} /> + + + { + if (layer.visibility === LAYER_VISIBILITY.VISIBLE) { + layer.visibility = LAYER_VISIBILITY.NONE; + setLayerVisibility( + new Map(layerVisibility.set(layer.id, false)) + ); + } else { + layer.visibility = LAYER_VISIBILITY.VISIBLE; + setLayerVisibility( + new Map(layerVisibility.set(layer.id, true)) + ); + } + layersFunctionMap[layer.type]?.hide(maplibreRef, layer); + }} + aria-label="Hide or show layer" + color="text" + /> + + + { + layersFunctionMap[layer.type]?.remove(maplibreRef, layer); + removeLayer(layer.id); + }} + aria-label="Delete layer" + color="text" + /> + + + + + - - -
- )} -
- ); - })} -
-
- {isLayerConfigVisible && selectedLayerConfig && ( - +
+ )} +
+ ); + })} +
+
+ {isLayerConfigVisible && selectedLayerConfig && ( + + )} + - )} - -
-
-
+ + + + ); + } + + return ( + + setIsLayerControlVisible((visible) => !visible)} + aria-label="Show layer control" + /> + ); } - - return ( - - setIsLayerControlVisible((visible) => !visible)} - aria-label="Show layer control" - /> - - ); -}); - -export { LayerControlPanel }; +); diff --git a/maps_dashboards/public/components/map_container/map_container.scss b/maps_dashboards/public/components/map_container/map_container.scss index be555172..79d39b58 100644 --- a/maps_dashboards/public/components/map_container/map_container.scss +++ b/maps_dashboards/public/components/map_container/map_container.scss @@ -8,7 +8,7 @@ /* stylelint-disable no-empty-source */ .map-container { width: 100%; - min-height: calc(100vh - 98px); + min-height: calc(100vh - 154px); } .maplibregl-ctrl-top-left { @@ -19,8 +19,8 @@ .layerControlPanel-container { z-index: 1; position: absolute; - left: $euiSizeS; - top: $euiSizeS; + margin-left: $euiSizeS; + margin-top: $euiSizeS; } .zoombar { diff --git a/maps_dashboards/public/components/map_container/map_container.tsx b/maps_dashboards/public/components/map_container/map_container.tsx index 8bb88570..629a8b66 100644 --- a/maps_dashboards/public/components/map_container/map_container.tsx +++ b/maps_dashboards/public/components/map_container/map_container.tsx @@ -10,15 +10,23 @@ import { LayerControlPanel } from '../layer_control_panel'; import './map_container.scss'; import { MAP_INITIAL_STATE, MAP_GLYPHS } from '../../../common'; import { MapLayerSpecification } from '../../model/mapLayerType'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; interface MapContainerProps { - mapIdFromUrl: string; setLayers: (layers: MapLayerSpecification[]) => void; layers: MapLayerSpecification[]; + layersIndexPatterns: IndexPattern[]; + setLayersIndexPatterns: (indexPatterns: IndexPattern[]) => void; + maplibreRef: React.MutableRefObject; } -export const MapContainer = ({ mapIdFromUrl, setLayers, layers }: MapContainerProps) => { - const maplibreRef = useRef(null); +export const MapContainer = ({ + setLayers, + layers, + layersIndexPatterns, + setLayersIndexPatterns, + maplibreRef, +}: MapContainerProps) => { const mapContainer = useRef(null); const [mounted, setMounted] = useState(false); const [zoom, setZoom] = useState(MAP_INITIAL_STATE.zoom); @@ -56,7 +64,13 @@ export const MapContainer = ({ mapIdFromUrl, setLayers, layers }: MapContainerPr
{mounted && ( - + )}
diff --git a/maps_dashboards/public/components/map_page/map_page.tsx b/maps_dashboards/public/components/map_page/map_page.tsx index 22ae0019..4d0fda15 100644 --- a/maps_dashboards/public/components/map_page/map_page.tsx +++ b/maps_dashboards/public/components/map_page/map_page.tsx @@ -3,23 +3,27 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; import { SimpleSavedObject } from 'opensearch-dashboards/public'; +import { Map as Maplibre } from 'maplibre-gl'; import { MapContainer } from '../map_container'; import { MapTopNavMenu } from '../map_top_nav'; import { MapLayerSpecification } from '../../model/mapLayerType'; import { MapServices } from '../../types'; import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public'; import { MapSavedObjectAttributes } from '../../../common/map_saved_object_attributes'; -import { OPENSEARCH_MAP_LAYER } from '../../../common'; +import { DASHBOARDS_MAPS_LAYER_TYPE, OPENSEARCH_MAP_LAYER } from '../../../common'; import { getLayerConfigMap } from '../../utils/getIntialLayerConfig'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; export const MapPage = () => { const [layers, setLayers] = useState([]); const { id: mapIdFromUrl } = useParams<{ id: string }>(); const [savedMapObject, setSavedMapObject] = useState | null>(); + const [layersIndexPatterns, setLayersIndexPatterns] = useState([]); + const maplibreRef = useRef(null); const { services } = useOpenSearchDashboards(); const { savedObjects: { client: savedObjectsClient }, @@ -29,7 +33,17 @@ export const MapPage = () => { if (mapIdFromUrl) { savedObjectsClient.get('map', mapIdFromUrl).then((res) => { setSavedMapObject(res); - setLayers(JSON.parse(res.attributes.layerList as string)); + const layerList = JSON.parse(res.attributes.layerList as string); + setLayers(layerList); + const savedIndexPatterns: IndexPattern[] = []; + layerList.forEach(async (layer: MapLayerSpecification) => { + if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS) { + const indexPatternId = layer.source.indexPatternId; + const indexPattern = await services.data.indexPatterns.get(indexPatternId); + savedIndexPatterns.push(indexPattern); + } + }); + setLayersIndexPatterns(savedIndexPatterns); }); } else { const initialDefaultLayer: MapLayerSpecification = @@ -40,8 +54,20 @@ export const MapPage = () => { return (
- - + +
); }; diff --git a/maps_dashboards/public/components/map_top_nav/top_nav_menu.tsx b/maps_dashboards/public/components/map_top_nav/top_nav_menu.tsx index 8f107034..afaa724b 100644 --- a/maps_dashboards/public/components/map_top_nav/top_nav_menu.tsx +++ b/maps_dashboards/public/components/map_top_nav/top_nav_menu.tsx @@ -5,20 +5,31 @@ import React, { useCallback, useEffect, useState } from 'react'; import { SimpleSavedObject } from 'opensearch-dashboards/public'; -import { PLUGIN_ID } from '../../../common'; +import { IndexPattern } from '../../../../../src/plugins/data/public'; +import { DASHBOARDS_MAPS_LAYER_TYPE, PLUGIN_ID } from '../../../common'; import { getTopNavConfig } from './get_top_nav_config'; import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public'; import { MapServices } from '../../types'; import { MapSavedObjectAttributes } from '../../../common/map_saved_object_attributes'; import { getSavedMapBreadcrumbs } from '../../utils/breadcrumbs'; +import { doDataLayerRender } from '../../model/DataLayerController'; +import { MapLayerSpecification } from '../../model/mapLayerType'; interface MapTopNavMenuProps { mapIdFromUrl: string; layers: any; savedMapObject: SimpleSavedObject | null | undefined; + layersIndexPatterns: IndexPattern[]; + maplibreRef: any; } -export const MapTopNavMenu = ({ mapIdFromUrl, savedMapObject, layers }: MapTopNavMenuProps) => { +export const MapTopNavMenu = ({ + mapIdFromUrl, + savedMapObject, + layers, + layersIndexPatterns, + maplibreRef, +}: MapTopNavMenuProps) => { const { services } = useOpenSearchDashboards(); const { setHeaderActionMenu, @@ -48,6 +59,15 @@ export const MapTopNavMenu = ({ mapIdFromUrl, savedMapObject, layers }: MapTopNa changeTitle(title || 'Create'); }, [savedMapObject, mapIdFromUrl, title, changeTitle]); + const handleRefresh = () => { + layers.forEach((layer: MapLayerSpecification) => { + if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { + return; + } + doDataLayerRender(layer, services, layersIndexPatterns, maplibreRef); + }); + }; + return ( ); }; diff --git a/maps_dashboards/public/model/DataLayerController.ts b/maps_dashboards/public/model/DataLayerController.ts new file mode 100644 index 00000000..d8b35ad5 --- /dev/null +++ b/maps_dashboards/public/model/DataLayerController.ts @@ -0,0 +1,67 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Map as Maplibre } from 'maplibre-gl'; +import { MapLayerSpecification } from './mapLayerType'; +import { DASHBOARDS_MAPS_LAYER_TYPE } from '../../common'; +import { + IOpenSearchDashboardsSearchResponse, + isCompleteResponse, +} from '../../../../src/plugins/data/common'; +import { layersFunctionMap } from './layersFunctions'; +import { MapServices } from '../types'; +import { IndexPattern } from '../../../../src/plugins/data/common'; + +interface MaplibreRef { + current: Maplibre | null; +} + +export const doDataLayerRender = async ( + layer: MapLayerSpecification, + { data, notifications }: MapServices, + layersIndexPatterns: IndexPattern[], + maplibreRef: MaplibreRef +) => { + if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS) { + const sourceConfig = layer.source; + const indexPatternRefName = sourceConfig?.indexPatternRefName; + const geoField = sourceConfig.geoFieldName; + const sourceFields: string[] = [geoField]; + if (sourceConfig.showTooltips && sourceConfig.tooltipFields.length > 0) { + sourceFields.push(...sourceConfig.tooltipFields); + } + const indexPattern = layersIndexPatterns.find((ip) => ip.id === sourceConfig.indexPatternId); + let dataQuery; + if (indexPattern) { + dataQuery = data.query.getOpenSearchQuery(indexPattern); + } + const request = { + params: { + index: indexPatternRefName, + size: layer.source.documentRequestNumber, + body: { + _source: sourceFields, + query: dataQuery, + }, + }, + }; + + const search$ = data.search.search(request).subscribe({ + next: (response: IOpenSearchDashboardsSearchResponse) => { + if (isCompleteResponse(response)) { + const dataSource = response.rawResponse.hits.hits; + layersFunctionMap[layer.type].render(maplibreRef, layer, dataSource); + search$.unsubscribe(); + } else { + notifications.toasts.addWarning('An error has occurred when query dataSource'); + search$.unsubscribe(); + } + }, + error: (e: Error) => { + data.search.showError(e); + }, + }); + } +}; diff --git a/maps_dashboards/public/model/mapLayerType.ts b/maps_dashboards/public/model/mapLayerType.ts index b5e47170..e827ef65 100644 --- a/maps_dashboards/public/model/mapLayerType.ts +++ b/maps_dashboards/public/model/mapLayerType.ts @@ -32,7 +32,7 @@ export type DocumentLayerSpecification = { visibility: string; source: { indexPatternRefName: string; - indexPatternId?: string; + indexPatternId: string; geoFieldType: 'geo_point' | 'geo_shape'; geoFieldName: string; documentRequestNumber: number;