From fea820237b5344213f55a824d13cd3176009b208 Mon Sep 17 00:00:00 2001 From: Junqiu Lei Date: Fri, 26 Apr 2024 10:14:45 -0700 Subject: [PATCH] Support multi data source in Region map (#6654) Signed-off-by: Junqiu Lei --- CHANGELOG.md | 1 + .../region_map/opensearch_dashboards.json | 5 ++- .../region_map/public/choropleth_layer.js | 12 ++++-- .../region_map/public/region_map_type.js | 26 ++++++++----- .../public/region_map_visualization.js | 7 ++-- src/plugins/region_map/public/services.ts | 31 +++++++++------ .../region_map/server/routes/opensearch.ts | 38 +++++++++++++++---- 7 files changed, 82 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 154b9d74fc9a..9a94b7279bb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,6 +106,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multiple Datasource] Modify selectable picker to remove group label and close popover after selection ([#6515](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6515)) - [Multiple Datasource] Extract the button component for datasource picker to avoid duplicate code ([#6559](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6559)) - [Workspace] Add workspaces filter to saved objects page. ([#6458](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6458)) +- [Multiple Datasource] Support multi data source in Region map ([#6654](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6654)) ### 🐛 Bug Fixes diff --git a/src/plugins/region_map/opensearch_dashboards.json b/src/plugins/region_map/opensearch_dashboards.json index b2859541377d..005714fe2b97 100644 --- a/src/plugins/region_map/opensearch_dashboards.json +++ b/src/plugins/region_map/opensearch_dashboards.json @@ -16,7 +16,8 @@ ], "requiredBundles": [ "opensearchDashboardsUtils", - "charts", + "charts", "visDefaultEditor" - ] + ], + "optionalPlugins": ["dataSource"] } diff --git a/src/plugins/region_map/public/choropleth_layer.js b/src/plugins/region_map/public/choropleth_layer.js index 84329953c9f0..b7f68c3986bb 100644 --- a/src/plugins/region_map/public/choropleth_layer.js +++ b/src/plugins/region_map/public/choropleth_layer.js @@ -100,7 +100,8 @@ export class ChoroplethLayer extends OpenSearchDashboardsMapLayer { leaflet, layerChosenByUser, http, - uiSettings + uiSettings, + dataSourceRefId ) { super(); this._serviceSettings = serviceSettings; @@ -119,6 +120,7 @@ export class ChoroplethLayer extends OpenSearchDashboardsMapLayer { this._http = http; this._visParams = null; this._uiSettings = uiSettings; + this._dataSourceRefId = dataSourceRefId; // eslint-disable-next-line no-undef this._leafletLayer = this._leaflet.geoJson(null, { @@ -249,7 +251,7 @@ CORS configuration of the server permits requests from the OpenSearch Dashboards try { const services = getServices(this._http); const indexSize = this._uiSettings.get(CUSTOM_VECTOR_MAP_MAX_SIZE_SETTING); - const result = await services.getIndexData(this._layerName, indexSize); + const result = await services.getIndexData(this._layerName, indexSize, this._dataSourceRefId); const finalResult = { type: 'FeatureCollection', @@ -346,7 +348,8 @@ CORS configuration of the server permits requests from the OpenSearch Dashboards leaflet, layerChosenByUser, http, - uiSettings + uiSettings, + dataSourceRefId ) { const clonedLayer = new ChoroplethLayer( name, @@ -359,7 +362,8 @@ CORS configuration of the server permits requests from the OpenSearch Dashboards leaflet, layerChosenByUser, http, - uiSettings + uiSettings, + dataSourceRefId ); clonedLayer.setJoinField(this._joinField); clonedLayer.setColorRamp(this._colorRamp); diff --git a/src/plugins/region_map/public/region_map_type.js b/src/plugins/region_map/public/region_map_type.js index 581bc1a178bd..903b84a6ec64 100644 --- a/src/plugins/region_map/public/region_map_type.js +++ b/src/plugins/region_map/public/region_map_type.js @@ -55,18 +55,18 @@ export function createRegionMapTypeDefinition(dependencies) { return arr1.concat(arr2).filter((item) => !arr1.includes(item) || !arr2.includes(item)); }; - const getCustomIndices = async () => { + const getCustomIndices = async (dataSourceRefId) => { try { - const result = await services.getCustomIndices(); + const result = await services.getCustomIndices(dataSourceRefId); return result.resp; } catch (e) { return false; } }; - const getJoinFields = async (indexName) => { + const getJoinFields = async (indexName, dataSourceRefId) => { try { - const result = await services.getIndexMapping(indexName); + const result = await services.getIndexMapping(indexName, dataSourceRefId); const properties = diffArray(Object.keys(result.resp[indexName].mappings.properties), [ 'location', ]); @@ -82,17 +82,17 @@ export function createRegionMapTypeDefinition(dependencies) { } }; - const addSchemaToCustomLayer = async (customlayer) => { - const joinFields = await getJoinFields(customlayer.index); + const addSchemaToCustomLayer = async (customlayer, dataSourceRefId) => { + const joinFields = await getJoinFields(customlayer, dataSourceRefId); const customLayerWithSchema = { attribution: 'Made with NaturalEarth', created_at: '2017-04-26T17:12:15.978370', format: 'geojson', fields: joinFields, - id: customlayer.index, + id: customlayer, meta: undefined, - name: customlayer.index, + name: customlayer, origin: 'user-upload', }; @@ -147,6 +147,7 @@ provided base maps, or add your own. Darker colors represent higher values.', vectorLayers: [], customVectorLayers: [], tmsLayers: [], + dataSourceRefId: '', }, schemas: new Schemas([ { @@ -199,7 +200,12 @@ provided base maps, or add your own. Darker colors represent higher values.', const customVectorLayers = regionmapsConfig.layers.map( mapToLayerWithId.bind(null, ORIGIN.OPENSEARCH_DASHBOARDS_YML) ); - const customIndices = await getCustomIndices(); + + // Read the data source reference id from index pattern if available, empty string means for local clusters + const dataSourceRefId = vis.data.indexPattern?.dataSourceRef?.id || ''; + vis.type.editorConfig.collections.dataSourceRefId = dataSourceRefId; + + const customIndices = await getCustomIndices(dataSourceRefId); let selectedLayer = vectorLayers[0]; let selectedJoinField = selectedLayer ? selectedLayer.fields[0] : null; @@ -214,7 +220,7 @@ provided base maps, or add your own. Darker colors represent higher values.', .filter( (layer) => !vectorLayers.some((vectorLayer) => vectorLayer.layerId === layer.layerId) ); - const promises = customIndices.map(addSchemaToCustomLayer); + const promises = customIndices.map((idx) => addSchemaToCustomLayer(idx, dataSourceRefId)); const newCustomLayers = await Promise.all(promises); // backfill v1 manifest for now diff --git a/src/plugins/region_map/public/region_map_visualization.js b/src/plugins/region_map/public/region_map_visualization.js index 69dca0525b89..e9fa55b47840 100644 --- a/src/plugins/region_map/public/region_map_visualization.js +++ b/src/plugins/region_map/public/region_map_visualization.js @@ -183,7 +183,6 @@ export function createRegionMapVisualization({ const metricFieldFormatter = getFormatService().deserialize(this._params.metric.format); this._choroplethLayer.setLayerChosenByUser(this._params.layerChosenByUser); - this._choroplethLayer.setJoinField(this._params.selectedJoinField.name); this._choroplethLayer.setColorRamp(truncatedColorMaps[this._params.colorSchema].value); this._choroplethLayer.setLineWeight(this._params.outlineWeight); @@ -231,7 +230,8 @@ export function createRegionMapVisualization({ (await lazyLoadMapsLegacyModules()).L, this._params.layerChosenByUser, http, - uiSettings + uiSettings, + this.vis.type.editorConfig.collections.dataSourceRefId ); } else { const { ChoroplethLayer } = await import('./choropleth_layer'); @@ -246,7 +246,8 @@ export function createRegionMapVisualization({ (await lazyLoadMapsLegacyModules()).L, this._params.layerChosenByUser, http, - uiSettings + uiSettings, + this.vis.type.editorConfig.collections.dataSourceRefId ); } this._choroplethLayer.setLayerChosenByUser(this._params.layerChosenByUser); diff --git a/src/plugins/region_map/public/services.ts b/src/plugins/region_map/public/services.ts index ba182c7aa0fa..549666981d4d 100644 --- a/src/plugins/region_map/public/services.ts +++ b/src/plugins/region_map/public/services.ts @@ -6,46 +6,53 @@ import { CoreStart, HttpFetchError } from 'opensearch-dashboards/public'; export interface Services { - getCustomIndices: () => Promise; - getIndexData: (indexName: string, size: number) => Promise; - getIndexMapping: (indexName: string) => Promise; + getCustomIndices: (dataSourceRefId: string) => Promise; + getIndexData: ( + indexName: string, + size: number, + dataSourceRefId: string + ) => Promise; + getIndexMapping: ( + indexName: string, + dataSourceRefId: string + ) => Promise; } export function getServices(http: CoreStart['http']): Services { return { - getCustomIndices: async () => { + getCustomIndices: async (dataSourceRefId: string) => { try { - const response = await http.post('../api/geospatial/_indices', { + return await http.post('../api/geospatial/_indices', { body: JSON.stringify({ index: '*-map', }), + query: { dataSourceId: dataSourceRefId }, }); - return response; } catch (e) { return e; } }, - getIndexData: async (indexName: string, size: number) => { + getIndexData: async (indexName: string, size: number, dataSourceRefId: string) => { try { - const response = await http.post('../api/geospatial/_search', { + return await http.post('../api/geospatial/_search', { body: JSON.stringify({ index: indexName, size, }), + query: { dataSourceId: dataSourceRefId }, }); - return response; } catch (e) { return e; } }, - getIndexMapping: async (indexName: string) => { + getIndexMapping: async (indexName: string, dataSourceRefId: string) => { try { - const response = await http.post('../api/geospatial/_mappings', { + return await http.post('../api/geospatial/_mappings', { body: JSON.stringify({ index: indexName, }), + query: { dataSourceId: dataSourceRefId }, }); - return response; } catch (e) { return e; } diff --git a/src/plugins/region_map/server/routes/opensearch.ts b/src/plugins/region_map/server/routes/opensearch.ts index dfdcb4a1900d..4ea868710999 100644 --- a/src/plugins/region_map/server/routes/opensearch.ts +++ b/src/plugins/region_map/server/routes/opensearch.ts @@ -14,20 +14,28 @@ export function registerGeospatialRoutes(router: IRouter) { body: schema.object({ index: schema.string(), }), + query: schema.maybe(schema.object({}, { unknowns: 'allow' })), }, }, async (context, req, res) => { - const client = context.core.opensearch.client.asCurrentUser; try { - const { index } = req.body; - const indices = await client.cat.indices({ - index, + let client; + // @ts-ignore + if (!req.query.dataSourceId) { + client = context.core.opensearch.client.asCurrentUser; + } else { + // @ts-ignore + client = await context.dataSource.opensearch.getClient(req.query.dataSourceId); + } + const response = await client.cat.indices({ + index: req.body.index, format: 'json', }); + const indexNames = response.body.map((index: any) => index.index); return res.ok({ body: { ok: true, - resp: indices.body, + resp: indexNames, }, }); } catch (err: any) { @@ -59,10 +67,18 @@ export function registerGeospatialRoutes(router: IRouter) { index: schema.string(), size: schema.number(), }), + query: schema.maybe(schema.object({}, { unknowns: 'allow' })), }, }, async (context, req, res) => { - const client = context.core.opensearch.client.asCurrentUser; + let client; + // @ts-ignore + if (!req.query.dataSourceId) { + client = context.core.opensearch.client.asCurrentUser; + } else { + // @ts-ignore + client = await context.dataSource.opensearch.getClient(req.query.dataSourceId); + } try { const { index, size } = req.body; const params = { index, body: {}, size }; @@ -91,10 +107,18 @@ export function registerGeospatialRoutes(router: IRouter) { body: schema.object({ index: schema.string(), }), + query: schema.maybe(schema.object({}, { unknowns: 'allow' })), }, }, async (context, req, res) => { - const client = context.core.opensearch.client.asCurrentUser; + let client; + // @ts-ignore + if (!req.query.dataSourceId) { + client = context.core.opensearch.client.asCurrentUser; + } else { + // @ts-ignore + client = await context.dataSource.opensearch.getClient(req.query.dataSourceId); + } try { const { index } = req.body; const mappings = await client.indices.getMapping({ index });