From 1b7605252aab430dd461f5f86fea9fdb956959ea Mon Sep 17 00:00:00 2001 From: Junqiu Lei Date: Wed, 21 Dec 2022 12:58:19 -0800 Subject: [PATCH] Integrate to saved object Signed-off-by: Junqiu Lei --- maps_dashboards/public/_variables.scss | 6 +++ .../add_layer_panel/add_layer_panel.tsx | 2 +- .../layer_config/layer_config_panel.tsx | 1 - .../layer_control_panel.tsx | 37 +++++++------- .../map_container/map_container.scss | 3 +- .../map_container/map_container.tsx | 4 ++ .../public/components/map_page/map_page.tsx | 19 ++++--- .../map_top_nav/get_top_nav_config.tsx | 13 ++++- .../components/map_top_nav/top_nav_menu.tsx | 49 ++++++++++++++++--- .../public/model/DataLayerController.ts | 22 ++++++--- maps_dashboards/public/model/mapState.ts | 13 +++++ ...ntialLayerConfig.ts => getIntialConfig.ts} | 21 ++++++++ 12 files changed, 149 insertions(+), 41 deletions(-) create mode 100644 maps_dashboards/public/_variables.scss create mode 100644 maps_dashboards/public/model/mapState.ts rename maps_dashboards/public/utils/{getIntialLayerConfig.ts => getIntialConfig.ts} (88%) diff --git a/maps_dashboards/public/_variables.scss b/maps_dashboards/public/_variables.scss new file mode 100644 index 00000000..98f04c0a --- /dev/null +++ b/maps_dashboards/public/_variables.scss @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +$mapHeaderOffset: 154px; diff --git a/maps_dashboards/public/components/add_layer_panel/add_layer_panel.tsx b/maps_dashboards/public/components/add_layer_panel/add_layer_panel.tsx index 37ed99c1..6e71e362 100644 --- a/maps_dashboards/public/components/add_layer_panel/add_layer_panel.tsx +++ b/maps_dashboards/public/components/add_layer_panel/add_layer_panel.tsx @@ -21,7 +21,7 @@ import { } from '@elastic/eui'; import './add_layer_panel.scss'; import { DOCUMENTS, OPENSEARCH_MAP_LAYER, CUSTOM_MAP, Layer } from '../../../common'; -import { getLayerConfigMap } from '../../utils/getIntialLayerConfig'; +import { getLayerConfigMap } from '../../utils/getIntialConfig'; interface Props { setIsLayerConfigVisible: Function; 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 a594ce78..99834193 100644 --- a/maps_dashboards/public/components/layer_config/layer_config_panel.tsx +++ b/maps_dashboards/public/components/layer_config/layer_config_panel.tsx @@ -78,7 +78,6 @@ export const LayerConfigPanel = ({ }; const onUpdate = () => { updateLayer(); - updateIndexPatterns(); closeLayerConfigPanel(false); }; 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 432866b4..b1cf25d3 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 @@ -36,12 +36,7 @@ import { layersFunctionMap } from '../../model/layersFunctions'; import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public'; import { MapServices } from '../../types'; import { doDataLayerRender } from '../../model/DataLayerController'; -import { - IOpenSearchDashboardsSearchResponse, - isCompleteResponse, - buildOpenSearchQuery, -} from '../../../../../src/plugins/data/common'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; +import { MapState } from '../../model/mapState'; interface MaplibreRef { current: Maplibre | null; @@ -53,10 +48,18 @@ interface Props { layers: MapLayerSpecification[]; layersIndexPatterns: IndexPattern[]; setLayersIndexPatterns: (indexPatterns: IndexPattern[]) => void; + mapState: MapState; } export const LayerControlPanel = memo( - ({ maplibreRef, setLayers, layers, layersIndexPatterns, setLayersIndexPatterns }: Props) => { + ({ + maplibreRef, + setLayers, + layers, + layersIndexPatterns, + setLayersIndexPatterns, + mapState, + }: Props) => { const { services } = useOpenSearchDashboards(); const { data: { indexPatterns }, @@ -79,7 +82,6 @@ export const LayerControlPanel = memo( if (layers.length <= 0) { return; } - if (initialLayersLoaded) { if (!selectedLayerConfig) { return; @@ -87,7 +89,8 @@ export const LayerControlPanel = memo( if (selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { layersFunctionMap[selectedLayerConfig.type].render(maplibreRef, selectedLayerConfig); } else { - doDataLayerRender(selectedLayerConfig, services, layersIndexPatterns, maplibreRef); + updateIndexPatterns(); + doDataLayerRender(selectedLayerConfig, mapState, services, maplibreRef); } if (addLayerId !== selectedLayerConfig.id) { setSelectedLayerConfig(undefined); @@ -97,7 +100,7 @@ export const LayerControlPanel = memo( if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { layersFunctionMap[layer.type].render(maplibreRef, layer); } else { - doDataLayerRender(layer, services, layersIndexPatterns, maplibreRef); + doDataLayerRender(layer, mapState, services, maplibreRef); } }); setInitialLayersLoaded(true); @@ -148,14 +151,9 @@ export const LayerControlPanel = memo( setSelectedLayerConfig(layer); setIsLayerConfigVisible(true); }; - const isLayerExists = (name: string) => { - return layers.findIndex((layer) => layer.name === name) > -1; - }; - - const onClickLayerName = (layer: MapLayerSpecification) => { - setSelectedLayerConfig(layer); - setIsLayerConfigVisible(true); - }; + const isLayerExists = (name: string) => { + return layers.findIndex((layer) => layer.name === name) > -1; + }; const [layerVisibility, setLayerVisibility] = useState(new Map([])); layers.forEach((layer) => { @@ -182,6 +180,9 @@ export const LayerControlPanel = memo( if (selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { return; } + if (!selectedLayerConfig.source.indexPatternId) { + return; + } const findIndexPattern = layersIndexPatterns.find( (indexPattern) => indexPattern.id === selectedLayerConfig.source.indexPatternId ); diff --git a/maps_dashboards/public/components/map_container/map_container.scss b/maps_dashboards/public/components/map_container/map_container.scss index 79d39b58..7453ca94 100644 --- a/maps_dashboards/public/components/map_container/map_container.scss +++ b/maps_dashboards/public/components/map_container/map_container.scss @@ -4,11 +4,12 @@ */ @import "maplibre-gl/dist/maplibre-gl.css"; +@import "../../variables"; /* stylelint-disable no-empty-source */ .map-container { width: 100%; - min-height: calc(100vh - 154px); + min-height: calc(100vh - #{$mapHeaderOffset}); } .maplibregl-ctrl-top-left { diff --git a/maps_dashboards/public/components/map_container/map_container.tsx b/maps_dashboards/public/components/map_container/map_container.tsx index 629a8b66..22f43a09 100644 --- a/maps_dashboards/public/components/map_container/map_container.tsx +++ b/maps_dashboards/public/components/map_container/map_container.tsx @@ -11,6 +11,7 @@ import './map_container.scss'; import { MAP_INITIAL_STATE, MAP_GLYPHS } from '../../../common'; import { MapLayerSpecification } from '../../model/mapLayerType'; import { IndexPattern } from '../../../../../src/plugins/data/public'; +import { MapState } from '../../model/mapState'; interface MapContainerProps { setLayers: (layers: MapLayerSpecification[]) => void; @@ -18,6 +19,7 @@ interface MapContainerProps { layersIndexPatterns: IndexPattern[]; setLayersIndexPatterns: (indexPatterns: IndexPattern[]) => void; maplibreRef: React.MutableRefObject; + mapState: MapState; } export const MapContainer = ({ @@ -26,6 +28,7 @@ export const MapContainer = ({ layersIndexPatterns, setLayersIndexPatterns, maplibreRef, + mapState, }: MapContainerProps) => { const mapContainer = useRef(null); const [mounted, setMounted] = useState(false); @@ -70,6 +73,7 @@ export const MapContainer = ({ setLayers={setLayers} layersIndexPatterns={layersIndexPatterns} setLayersIndexPatterns={setLayersIndexPatterns} + mapState={mapState} /> )} diff --git a/maps_dashboards/public/components/map_page/map_page.tsx b/maps_dashboards/public/components/map_page/map_page.tsx index 4d0fda15..28d073f1 100644 --- a/maps_dashboards/public/components/map_page/map_page.tsx +++ b/maps_dashboards/public/components/map_page/map_page.tsx @@ -14,26 +14,30 @@ import { MapServices } from '../../types'; import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public'; import { MapSavedObjectAttributes } from '../../../common/map_saved_object_attributes'; import { DASHBOARDS_MAPS_LAYER_TYPE, OPENSEARCH_MAP_LAYER } from '../../../common'; -import { getLayerConfigMap } from '../../utils/getIntialLayerConfig'; +import { getLayerConfigMap, getInitialMapState } from '../../utils/getIntialConfig'; import { IndexPattern } from '../../../../../src/plugins/data/public'; +import { MapState } from '../../model/mapState'; export const MapPage = () => { + const { services } = useOpenSearchDashboards(); + const { + savedObjects: { client: savedObjectsClient }, + } = services; 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 }, - } = services; + const [mapState, setMapState] = useState(getInitialMapState()); useEffect(() => { if (mapIdFromUrl) { savedObjectsClient.get('map', mapIdFromUrl).then((res) => { setSavedMapObject(res); - const layerList = JSON.parse(res.attributes.layerList as string); + const layerList: MapLayerSpecification[] = JSON.parse(res.attributes.layerList as string); + const savedMapState: MapState = JSON.parse(res.attributes.mapState as string); + setMapState(savedMapState); setLayers(layerList); const savedIndexPatterns: IndexPattern[] = []; layerList.forEach(async (layer: MapLayerSpecification) => { @@ -60,6 +64,8 @@ export const MapPage = () => { layers={layers} layersIndexPatterns={layersIndexPatterns} maplibreRef={maplibreRef} + mapState={mapState} + setMapState={setMapState} /> { layersIndexPatterns={layersIndexPatterns} setLayersIndexPatterns={setLayersIndexPatterns} maplibreRef={maplibreRef} + mapState={mapState} /> ); diff --git a/maps_dashboards/public/components/map_top_nav/get_top_nav_config.tsx b/maps_dashboards/public/components/map_top_nav/get_top_nav_config.tsx index 3b7ef206..4269c356 100644 --- a/maps_dashboards/public/components/map_top_nav/get_top_nav_config.tsx +++ b/maps_dashboards/public/components/map_top_nav/get_top_nav_config.tsx @@ -13,6 +13,7 @@ import { checkForDuplicateTitle, } from '../../../../../src/plugins/saved_objects/public'; import { MapServices } from '../../types'; +import { MapState } from '../../model/mapState'; const SAVED_OBJECT_TYPE = 'map'; @@ -23,6 +24,7 @@ interface GetTopNavConfigParams { description: string; setTitle: (title: string) => void; setDescription: (description: string) => void; + mapState: MapState; } export const getTopNavConfig = ( @@ -33,7 +35,15 @@ export const getTopNavConfig = ( history, overlays, }: MapServices, - { mapIdFromUrl, layers, title, description, setTitle, setDescription }: GetTopNavConfigParams + { + mapIdFromUrl, + layers, + title, + description, + setTitle, + setDescription, + mapState, + }: GetTopNavConfigParams ) => { const topNavConfig: TopNavMenuData[] = [ { @@ -50,6 +60,7 @@ export const getTopNavConfig = ( title: newTitle, description: newDescription, layerList: JSON.stringify(layers), + mapState: JSON.stringify(mapState), }; try { await checkForDuplicateTitle( 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 9a5d2506..a8dd0724 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,7 +5,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { SimpleSavedObject } from 'opensearch-dashboards/public'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; +import { IndexPattern, Query, TimeRange } 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'; @@ -14,13 +14,16 @@ import { MapSavedObjectAttributes } from '../../../common/map_saved_object_attri import { getSavedMapBreadcrumbs } from '../../utils/breadcrumbs'; import { doDataLayerRender } from '../../model/DataLayerController'; import { MapLayerSpecification } from '../../model/mapLayerType'; +import { MapState } from '../../model/mapState'; interface MapTopNavMenuProps { mapIdFromUrl: string; - layers: any; + layers: MapLayerSpecification[]; savedMapObject: SimpleSavedObject | null | undefined; layersIndexPatterns: IndexPattern[]; maplibreRef: any; + mapState: MapState; + setMapState: (mapState: MapState) => void; } export const MapTopNavMenu = ({ @@ -29,6 +32,8 @@ export const MapTopNavMenu = ({ layers, layersIndexPatterns, maplibreRef, + mapState, + setMapState, }: MapTopNavMenuProps) => { const { services } = useOpenSearchDashboards(); const { @@ -42,7 +47,11 @@ export const MapTopNavMenu = ({ const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); - + const [timeFrom, setTimeFrom] = useState(''); + const [timeTo, setTimeTo] = useState(''); + const [queryConfig, setQueryConfig] = useState({ query: '', language: 'kuery' }); + const [refreshIntervalValue, setRefreshIntervalValue] = useState(60000); + const [isRefreshPaused, setIsRefreshPaused] = useState(false); const changeTitle = useCallback( (newTitle: string) => { chrome.setBreadcrumbs(getSavedMapBreadcrumbs(newTitle, navigateToApp)); @@ -62,15 +71,33 @@ export const MapTopNavMenu = ({ changeTitle(title || 'Create'); }, [title, changeTitle]); - const handleRefresh = () => { + const refreshDataLayerRender = useCallback(() => { layers.forEach((layer: MapLayerSpecification) => { if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { return; } - doDataLayerRender(layer, services, layersIndexPatterns, maplibreRef); + doDataLayerRender(layer, mapState, services, maplibreRef); }); + }, [mapState]); + + const handleQuerySubmit = ({ query, dateRange }: { query?: Query; dateRange: TimeRange }) => { + if (query) { + setMapState({ ...mapState, query }); + } + if (dateRange) { + setMapState({ ...mapState, timeFilters: dateRange }); + } }; + useEffect(() => { + setTimeFrom(mapState.timeFilters.from); + setTimeTo(mapState.timeFilters.to); + setQueryConfig(mapState.query); + setIsRefreshPaused(mapState.refreshInterval.pause); + setRefreshIntervalValue(mapState.refreshInterval.value); + refreshDataLayerRender(); + }, [mapState, refreshDataLayerRender]); + return ( ); }; diff --git a/maps_dashboards/public/model/DataLayerController.ts b/maps_dashboards/public/model/DataLayerController.ts index d8b35ad5..953276ba 100644 --- a/maps_dashboards/public/model/DataLayerController.ts +++ b/maps_dashboards/public/model/DataLayerController.ts @@ -7,12 +7,14 @@ import { Map as Maplibre } from 'maplibre-gl'; import { MapLayerSpecification } from './mapLayerType'; import { DASHBOARDS_MAPS_LAYER_TYPE } from '../../common'; import { + buildOpenSearchQuery, + getTime, IOpenSearchDashboardsSearchResponse, isCompleteResponse, } from '../../../../src/plugins/data/common'; import { layersFunctionMap } from './layersFunctions'; import { MapServices } from '../types'; -import { IndexPattern } from '../../../../src/plugins/data/common'; +import { MapState } from './mapState'; interface MaplibreRef { current: Maplibre | null; @@ -20,22 +22,30 @@ interface MaplibreRef { export const doDataLayerRender = async ( layer: MapLayerSpecification, + mapState: MapState, { data, notifications }: MapServices, - layersIndexPatterns: IndexPattern[], maplibreRef: MaplibreRef ) => { if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS) { const sourceConfig = layer.source; + const indexPattern = await data.indexPatterns.get(sourceConfig.indexPatternId); 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; + let buildQuery; if (indexPattern) { - dataQuery = data.query.getOpenSearchQuery(indexPattern); + const timeFilters = getTime(indexPattern, mapState.timeFilters); + buildQuery = buildOpenSearchQuery( + indexPattern, + [], + [ + ...(layer.source.filters ? layer.source.filters : []), + ...(timeFilters ? [timeFilters] : []), + ] + ); } const request = { params: { @@ -43,7 +53,7 @@ export const doDataLayerRender = async ( size: layer.source.documentRequestNumber, body: { _source: sourceFields, - query: dataQuery, + query: buildQuery, }, }, }; diff --git a/maps_dashboards/public/model/mapState.ts b/maps_dashboards/public/model/mapState.ts new file mode 100644 index 00000000..4b9c8f51 --- /dev/null +++ b/maps_dashboards/public/model/mapState.ts @@ -0,0 +1,13 @@ +import { Query } from '../../../../src/plugins/data/common'; + +export interface MapState { + timeFilters: { + from: string; + to: string; + }; + query: Query; + refreshInterval: { + pause: boolean; + value: number; + }; +} diff --git a/maps_dashboards/public/utils/getIntialLayerConfig.ts b/maps_dashboards/public/utils/getIntialConfig.ts similarity index 88% rename from maps_dashboards/public/utils/getIntialLayerConfig.ts rename to maps_dashboards/public/utils/getIntialConfig.ts index cf070848..7466c17d 100644 --- a/maps_dashboards/public/utils/getIntialLayerConfig.ts +++ b/maps_dashboards/public/utils/getIntialConfig.ts @@ -21,6 +21,7 @@ import { OPENSEARCH_MAP_LAYER, CUSTOM_MAP, } from '../../common'; +import { MapState } from '../model/mapState'; export const getLayerConfigMap = () => ({ [OPENSEARCH_MAP_LAYER.type]: { @@ -97,3 +98,23 @@ export const getStyleColor = () => { borderColor: initialColor, }; }; + +export const getInitialMapState = (): MapState => { + const timeFilters = { + from: 'now-15m', + to: 'now', + }; + const query = { + query: '', + language: 'kuery', + }; + const refreshInterval = { + pause: true, + value: 12000, + }; + return { + timeFilters, + query, + refreshInterval, + }; +};