From 4d31e302be1f948f69f9ebc66020c5fccf35e90c Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 10 Apr 2023 19:06:48 -0600 Subject: [PATCH] [Visualize] fix tile_map visualization (#154392) Fixes https://github.com/elastic/kibana/issues/154375 ### background https://github.com/elastic/kibana/pull/105326 replaced tile_map visualization implementation with a new implementation that is a wrapper around MapEmbeddable. https://github.com/elastic/kibana/pull/152952 removed geohash_grid aggregation. This causes a regression where existing tile_map visualizations no longer work. Even though geohash_grid aggregation is no longer used, the AggType is still needed so that new tile_map visualization wrapper can access aggregation configuration state. This PR adds back geohash_grid AggType in `legacyAggs` for this purpose. PR also adds a functional test to better prevent regressions with tile_map ### Test * install web logs sample data * install legacy tile_map saved objects from ``` {"attributes":{"fieldFormatMap":"{\"hour_of_day\":{}}","runtimeFieldMap":"{\"hour_of_day\":{\"type\":\"long\",\"script\":{\"source\":\"emit(doc['timestamp'].value.getHour());\"}}}","timeFieldName":"timestamp","title":"kibana_sample_data_logs"},"coreMigrationVersion":"7.17.5","id":"90943e30-9a47-11e8-b64d-95841ca0b247","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"type":"index-pattern","updated_at":"2022-08-17T20:25:52.585Z","version":"WzEzMDQsMV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"region_map","uiStateJSON":"{}","version":1,"visState":"{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"field\":\"geo.dest\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":5},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"addTooltip\":true,\"colorSchema\":\"Yellow to Red\",\"emsHotLink\":\"https://maps.elastic.co/v7.16?locale=en#file/world_countries\",\"isDisplayWarning\":true,\"legendPosition\":\"bottomright\",\"mapCenter\":[0,0],\"mapZoom\":2,\"outlineWeight\":1,\"selectedJoinField\":{\"description\":\"ISO 3166-1 alpha-2 code\",\"name\":\"iso2\",\"type\":\"id\"},\"selectedLayer\":{\"attribution\":\"Made with NaturalEarth | OpenStreetMap contributors | Elastic Maps Service\",\"created_at\":\"2020-10-28T16:16:08.720286\",\"fields\":[{\"description\":\"ISO 3166-1 alpha-2 code\",\"name\":\"iso2\",\"type\":\"id\"},{\"description\":\"ISO 3166-1 alpha-3 code\",\"name\":\"iso3\",\"type\":\"id\"},{\"description\":\"ISO 3166-1 numeric code\",\"name\":\"iso_numeric\",\"type\":\"id\"},{\"description\":\"name\",\"name\":\"name\",\"type\":\"property\"}],\"format\":\"topojson\",\"id\":\"world_countries\",\"isEMS\":true,\"layerId\":\"elastic_maps_service.World Countries\",\"meta\":{\"feature_collection_path\":\"data\"},\"name\":\"World Countries\",\"origin\":\"elastic_maps_service\"},\"showAllShapes\":true,\"wms\":{\"enabled\":false,\"options\":{\"attribution\":\"\",\"format\":\"image/png\",\"layers\":\"\",\"styles\":\"\",\"transparent\":true,\"version\":\"\"},\"selectedTmsLayer\":{\"attribution\":\"OpenStreetMap contributors | OpenMapTiles | Elastic Maps Service\",\"id\":\"road_map\",\"maxZoom\":20,\"minZoom\":0,\"origin\":\"elastic_maps_service\"},\"url\":\"\"}},\"title\":\"region_map\",\"type\":\"region_map\"}"},"coreMigrationVersion":"7.17.5","id":"64a5b9f0-1e6b-11ed-833b-a105e9534fa9","migrationVersion":{"visualization":"7.17.0"},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2022-08-17T20:30:50.288Z","version":"WzE0MDIsMV0="} {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"tile_map","uiStateJSON":"{\"mapZoom\":2,\"mapCenter\":[13.64385981167601,-135.97675761558068]}","version":1,"visState":"{\"title\":\"tile_map\",\"type\":\"tile_map\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"geohash_grid\",\"params\":{\"field\":\"geo.coordinates\",\"autoPrecision\":true,\"precision\":2,\"useGeocentroid\":true,\"isFilteredByCollar\":true},\"schema\":\"segment\"}],\"params\":{\"colorSchema\":\"Yellow to Red\",\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":true,\"addTooltip\":true,\"heatClusterSize\":1.5,\"legendPosition\":\"bottomright\",\"mapZoom\":2,\"mapCenter\":[0,0],\"wms\":{\"enabled\":false,\"url\":\"\",\"options\":{\"version\":\"\",\"layers\":\"\",\"format\":\"image/png\",\"transparent\":true,\"attribution\":\"\",\"styles\":\"\"},\"selectedTmsLayer\":{\"origin\":\"elastic_maps_service\",\"id\":\"road_map\",\"minZoom\":0,\"maxZoom\":20,\"attribution\":\"OpenStreetMap contributors | OpenMapTiles | Elastic Maps Service\"}}}}"},"coreMigrationVersion":"7.17.5","id":"4b073190-1e6b-11ed-833b-a105e9534fa9","migrationVersion":{"visualization":"7.17.0"},"references":[{"id":"90943e30-9a47-11e8-b64d-95841ca0b247","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2022-08-17T20:29:28.769Z","version":"WzEzNDksMV0="} {"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"optionsJSON":"{\"useMargins\":true,\"syncColors\":false,\"hidePanelTitles\":false}","panelsJSON":"[{\"version\":\"7.17.5\",\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":15,\"i\":\"ad02c99b-8bae-42e0-8a43-d27762f1e607\"},\"panelIndex\":\"ad02c99b-8bae-42e0-8a43-d27762f1e607\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_ad02c99b-8bae-42e0-8a43-d27762f1e607\"},{\"version\":\"7.17.5\",\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"8412ffc8-b94c-4bbd-aa55-fa670f3fb4ee\"},\"panelIndex\":\"8412ffc8-b94c-4bbd-aa55-fa670f3fb4ee\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_8412ffc8-b94c-4bbd-aa55-fa670f3fb4ee\"}]","timeRestore":false,"title":"dash with legacy map visualizations","version":1},"coreMigrationVersion":"7.17.5","id":"97437d70-1e6b-11ed-833b-a105e9534fa9","migrationVersion":{"dashboard":"7.17.3"},"references":[{"id":"64a5b9f0-1e6b-11ed-833b-a105e9534fa9","name":"ad02c99b-8bae-42e0-8a43-d27762f1e607:panel_ad02c99b-8bae-42e0-8a43-d27762f1e607","type":"visualization"},{"id":"4b073190-1e6b-11ed-833b-a105e9534fa9","name":"8412ffc8-b94c-4bbd-aa55-fa670f3fb4ee:panel_8412ffc8-b94c-4bbd-aa55-fa670f3fb4ee","type":"visualization"}],"type":"dashboard","updated_at":"2022-08-17T20:31:36.656Z","version":"WzE0MzEsMV0="} {"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":4,"missingRefCount":0,"missingReferences":[]} ``` * Open dashboard and verify tile_map renders --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../common/search/aggs/agg_configs.test.ts | 2 +- .../common/search/aggs/agg_types_registry.ts | 30 ++++++++++++---- .../common/search/aggs/aggs_service.test.ts | 2 +- .../search_source/search_source.test.ts | 20 +++++------ .../data/common/search/tabify/tabify.test.ts | 2 +- .../public/search/aggs/aggs_service.test.ts | 2 +- src/plugins/data/public/search/aggs/mocks.ts | 1 + .../server/search/aggs/aggs_service.test.ts | 2 +- src/plugins/data/server/search/aggs/mocks.ts | 1 + .../datatable/components/types.ts | 2 +- .../visualizations/datatable/expression.tsx | 2 +- .../public/legacy_visualizations/index.ts | 1 + .../tile_map/geo_hash.ts | 29 +++++++++++++++ .../legacy_visualizations/tile_map/index.ts | 1 + .../legacy_visualizations/tile_map/utils.ts | 3 +- x-pack/plugins/maps/public/plugin.ts | 6 +++- x-pack/plugins/maps/tsconfig.json | 1 + .../test/functional/apps/maps/group4/index.js | 1 + .../functional/apps/maps/group4/tile_map.ts | 35 +++++++++++++++++++ .../fixtures/kbn_archiver/maps.json | 27 ++++++++++++++ 20 files changed, 145 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/maps/public/legacy_visualizations/tile_map/geo_hash.ts create mode 100644 x-pack/test/functional/apps/maps/group4/tile_map.ts diff --git a/src/plugins/data/common/search/aggs/agg_configs.test.ts b/src/plugins/data/common/search/aggs/agg_configs.test.ts index c3b7ffe937b35..441893affa2c3 100644 --- a/src/plugins/data/common/search/aggs/agg_configs.test.ts +++ b/src/plugins/data/common/search/aggs/agg_configs.test.ts @@ -96,7 +96,7 @@ describe('AggConfigs', () => { ac.createAggConfig( new AggConfig(ac, { enabled: true, - type: typesRegistry.get('terms'), + type: typesRegistry.get('terms')!, params: {}, schema: 'split', }) diff --git a/src/plugins/data/common/search/aggs/agg_types_registry.ts b/src/plugins/data/common/search/aggs/agg_types_registry.ts index 24521a8d564ef..d4872188e4b2c 100644 --- a/src/plugins/data/common/search/aggs/agg_types_registry.ts +++ b/src/plugins/data/common/search/aggs/agg_types_registry.ts @@ -16,6 +16,7 @@ export type AggTypesRegistryStart = ReturnType; export class AggTypesRegistry { private readonly bucketAggs = new Map(); private readonly metricAggs = new Map(); + private readonly legacyAggs = new Map(); setup = () => { return { @@ -44,13 +45,28 @@ export class AggTypesRegistry { } this.metricAggs.set(name, type); }, + registerLegacy: < + N extends string, + T extends (deps: AggTypesDependencies) => BucketAggType | MetricAggType + >( + name: N, + type: T + ): void => { + if (this.legacyAggs.get(name) || this.legacyAggs.get(name)) { + throw new Error(`Agg has already been registered with name: ${name}`); + } + this.legacyAggs.set(name, type); + }, }; }; start = (aggTypesDependencies: AggTypesDependencies) => { const initializedAggTypes = new Map(); - const getInitializedFromCache = (key: string, agg: any): T => { + const getInitializedFromCache = ( + key: string, + agg: (aggTypesDependencies: AggTypesDependencies) => T + ): T => { if (initializedAggTypes.has(key)) { return initializedAggTypes.get(key); } @@ -60,11 +76,13 @@ export class AggTypesRegistry { }; return { - get: (name: string) => - getInitializedFromCache | MetricAggType>( - name, - this.bucketAggs.get(name) || this.metricAggs.get(name) - ), + get: (name: string) => { + const agg = + this.bucketAggs.get(name) || this.metricAggs.get(name) || this.legacyAggs.get(name); + return agg + ? getInitializedFromCache | MetricAggType>(name, agg) + : undefined; + }, getAll: () => ({ buckets: Array.from(this.bucketAggs.entries()).map(([key, value]) => getInitializedFromCache>(key, value) diff --git a/src/plugins/data/common/search/aggs/aggs_service.test.ts b/src/plugins/data/common/search/aggs/aggs_service.test.ts index c7f3fc54bfaf4..ebc357a20571a 100644 --- a/src/plugins/data/common/search/aggs/aggs_service.test.ts +++ b/src/plugins/data/common/search/aggs/aggs_service.test.ts @@ -208,7 +208,7 @@ describe('Aggs service', () => { test('types registry returns initialized type providers', () => { service.setup(setupDeps); const start = service.start(startDeps); - expect(start.types.get('terms').name).toBe('terms'); + expect(start.types.get('terms')?.name).toBe('terms'); }); }); }); diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index af83fb6c6de4a..f4146cc5634de 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -1236,7 +1236,7 @@ describe('SearchSource', () => { test('doesnt call any post flight requests if disabled', async () => { const typesRegistry = mockAggTypesRegistry(); - typesRegistry.get('avg').postFlightRequest = jest.fn(); + typesRegistry.get('avg')!.postFlightRequest = jest.fn(); const ac = getAggConfigs(typesRegistry, false); searchSource = new SearchSource({}, searchSourceDependencies); @@ -1251,12 +1251,12 @@ describe('SearchSource', () => { expect(fetchSub.error).toHaveBeenCalledTimes(0); expect(searchSourceDependencies.onResponse).toBeCalledTimes(1); - expect(typesRegistry.get('avg').postFlightRequest).toHaveBeenCalledTimes(0); + expect(typesRegistry.get('avg')!.postFlightRequest).toHaveBeenCalledTimes(0); }); test('doesnt call any post flight if searchsource has error', async () => { const typesRegistry = mockAggTypesRegistry(); - typesRegistry.get('avg').postFlightRequest = jest.fn(); + typesRegistry.get('avg')!.postFlightRequest = jest.fn(); const ac = getAggConfigs(typesRegistry, true); searchSourceDependencies.search = jest.fn().mockImplementation(() => @@ -1278,12 +1278,12 @@ describe('SearchSource', () => { expect(fetchSub.complete).toHaveBeenCalledTimes(0); expect(fetchSub.error).toHaveBeenNthCalledWith(1, 1); - expect(typesRegistry.get('avg').postFlightRequest).toHaveBeenCalledTimes(0); + expect(typesRegistry.get('avg')!.postFlightRequest).toHaveBeenCalledTimes(0); }); test('calls post flight requests, fires 1 extra response, returns last response', async () => { const typesRegistry = mockAggTypesRegistry(); - typesRegistry.get('avg').postFlightRequest = jest.fn().mockResolvedValue({ + typesRegistry.get('avg')!.postFlightRequest = jest.fn().mockResolvedValue({ other: 5, }); @@ -1325,12 +1325,12 @@ describe('SearchSource', () => { expect(fetchSub.complete).toHaveBeenCalledTimes(1); expect(fetchSub.error).toHaveBeenCalledTimes(0); expect(resp.rawResponse).toStrictEqual({ other: 5 }); - expect(typesRegistry.get('avg').postFlightRequest).toHaveBeenCalledTimes(3); + expect(typesRegistry.get('avg')!.postFlightRequest).toHaveBeenCalledTimes(3); }); test('calls post flight requests only once, with multiple subs (shareReplay)', async () => { const typesRegistry = mockAggTypesRegistry(); - typesRegistry.get('avg').postFlightRequest = jest.fn().mockResolvedValue({ + typesRegistry.get('avg')!.postFlightRequest = jest.fn().mockResolvedValue({ other: 5, }); @@ -1366,12 +1366,12 @@ describe('SearchSource', () => { expect(fetchSub.next).toHaveBeenCalledTimes(3); expect(fetchSub.complete).toHaveBeenCalledTimes(1); - expect(typesRegistry.get('avg').postFlightRequest).toHaveBeenCalledTimes(1); + expect(typesRegistry.get('avg')!.postFlightRequest).toHaveBeenCalledTimes(1); }); test('calls post flight requests, handles error', async () => { const typesRegistry = mockAggTypesRegistry(); - typesRegistry.get('avg').postFlightRequest = jest.fn().mockRejectedValue(undefined); + typesRegistry.get('avg')!.postFlightRequest = jest.fn().mockRejectedValue(undefined); const ac = getAggConfigs(typesRegistry, true); searchSource = new SearchSource({}, searchSourceDependencies); @@ -1385,7 +1385,7 @@ describe('SearchSource', () => { expect(fetchSub.next).toHaveBeenCalledTimes(2); expect(fetchSub.complete).toHaveBeenCalledTimes(0); expect(fetchSub.error).toHaveBeenCalledTimes(1); - expect(typesRegistry.get('avg').postFlightRequest).toHaveBeenCalledTimes(1); + expect(typesRegistry.get('avg')!.postFlightRequest).toHaveBeenCalledTimes(1); }); }); }); diff --git a/src/plugins/data/common/search/tabify/tabify.test.ts b/src/plugins/data/common/search/tabify/tabify.test.ts index 7b3f51274d148..4230782f60209 100644 --- a/src/plugins/data/common/search/tabify/tabify.test.ts +++ b/src/plugins/data/common/search/tabify/tabify.test.ts @@ -103,7 +103,7 @@ describe('tabifyAggResponse Integration', () => { describe(`scaleMetricValues performance check${getTitlePostfix()}`, () => { beforeAll(() => { - typesRegistry.get('count').params.push({ + typesRegistry.get('count')!.params.push({ name: 'scaleMetricValues', default: false, write: () => {}, diff --git a/src/plugins/data/public/search/aggs/aggs_service.test.ts b/src/plugins/data/public/search/aggs/aggs_service.test.ts index e348779d6190d..cbd0cbc40eb03 100644 --- a/src/plugins/data/public/search/aggs/aggs_service.test.ts +++ b/src/plugins/data/public/search/aggs/aggs_service.test.ts @@ -88,7 +88,7 @@ describe('AggsService - public', () => { service.setup(setupDeps); const start = service.start(startDeps); - expect(start.types.get('terms').name).toBe('terms'); + expect(start.types.get('terms')?.name).toBe('terms'); }); test('registers default agg types', () => { diff --git a/src/plugins/data/public/search/aggs/mocks.ts b/src/plugins/data/public/search/aggs/mocks.ts index 14444a4c0c2f5..9bdd59b303aa0 100644 --- a/src/plugins/data/public/search/aggs/mocks.ts +++ b/src/plugins/data/public/search/aggs/mocks.ts @@ -40,6 +40,7 @@ const aggTypeConfigMock = () => ({ export const aggTypesRegistrySetupMock = (): AggTypesRegistrySetup => ({ registerBucket: jest.fn(), registerMetric: jest.fn(), + registerLegacy: jest.fn(), }); export const aggTypesRegistryStartMock = (): AggTypesRegistryStart => ({ diff --git a/src/plugins/data/server/search/aggs/aggs_service.test.ts b/src/plugins/data/server/search/aggs/aggs_service.test.ts index a78416c6f0625..ae4b0cfd74995 100644 --- a/src/plugins/data/server/search/aggs/aggs_service.test.ts +++ b/src/plugins/data/server/search/aggs/aggs_service.test.ts @@ -69,7 +69,7 @@ describe('AggsService - server', () => { {} as ElasticsearchClient ); - expect(start.types.get('terms').name).toBe('terms'); + expect(start.types.get('terms')?.name).toBe('terms'); }); test('registers default agg types', async () => { diff --git a/src/plugins/data/server/search/aggs/mocks.ts b/src/plugins/data/server/search/aggs/mocks.ts index 093493c9b3bbf..f0757a69e80db 100644 --- a/src/plugins/data/server/search/aggs/mocks.ts +++ b/src/plugins/data/server/search/aggs/mocks.ts @@ -42,6 +42,7 @@ const aggTypeConfigMock = () => ({ export const aggTypesRegistrySetupMock = (): AggTypesRegistrySetup => ({ registerBucket: jest.fn(), registerMetric: jest.fn(), + registerLegacy: jest.fn(), }); export const aggTypesRegistryStartMock = (): AggTypesRegistryStart => ({ diff --git a/x-pack/plugins/lens/public/visualizations/datatable/components/types.ts b/x-pack/plugins/lens/public/visualizations/datatable/components/types.ts index 28cd16dc45e44..be6c354fd8449 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/components/types.ts +++ b/x-pack/plugins/lens/public/visualizations/datatable/components/types.ts @@ -50,7 +50,7 @@ export type LensPagesizeAction = LensEditEvent export type DatatableRenderProps = DatatableProps & { formatFactory: FormatFactory; dispatchEvent: ILensInterpreterRenderHandlers['event']; - getType: (name: string) => IAggType; + getType: (name: string) => IAggType | undefined; renderMode: RenderMode; paletteService: PaletteRegistry; uiSettings: IUiSettingsClient; diff --git a/x-pack/plugins/lens/public/visualizations/datatable/expression.tsx b/x-pack/plugins/lens/public/visualizations/datatable/expression.tsx index 34cd1559969a6..ee1ab20949d8a 100644 --- a/x-pack/plugins/lens/public/visualizations/datatable/expression.tsx +++ b/x-pack/plugins/lens/public/visualizations/datatable/expression.tsx @@ -76,7 +76,7 @@ export async function getColumnCellValueActions( export const getDatatableRenderer = (dependencies: { formatFactory: FormatFactory; - getType: Promise<(name: string) => IAggType>; + getType: Promise<(name: string) => IAggType | undefined>; paletteService: PaletteRegistry; uiSettings: IUiSettingsClient; theme: ThemeServiceStart; diff --git a/x-pack/plugins/maps/public/legacy_visualizations/index.ts b/x-pack/plugins/maps/public/legacy_visualizations/index.ts index f6e154949390e..d99544bbb207d 100644 --- a/x-pack/plugins/maps/public/legacy_visualizations/index.ts +++ b/x-pack/plugins/maps/public/legacy_visualizations/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +export { GEOHASH_GRID, getGeoHashBucketAgg } from './tile_map'; export { createRegionMapFn, regionMapRenderer, regionMapVisType } from './region_map'; export { createTileMapFn, tileMapRenderer, tileMapVisType } from './tile_map'; export { isLegacyMap } from './is_legacy_map'; diff --git a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/geo_hash.ts b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/geo_hash.ts new file mode 100644 index 0000000000000..3b8be63278179 --- /dev/null +++ b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/geo_hash.ts @@ -0,0 +1,29 @@ +/* + * 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 { KBN_FIELD_TYPES } from '@kbn/field-types'; +import { BucketAggType, type IBucketAggConfig } from '@kbn/data-plugin/common'; + +export const GEOHASH_GRID = 'geohash_grid'; + +export const getGeoHashBucketAgg = () => + new BucketAggType({ + name: GEOHASH_GRID, + expressionName: '', + title: GEOHASH_GRID, + makeLabel: () => GEOHASH_GRID, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT, + }, + ], + getRequestAggs(agg) { + return []; + }, + }); diff --git a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/index.ts b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/index.ts index 04d4c160fb510..78fcd0bd8d17e 100644 --- a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/index.ts +++ b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +export { GEOHASH_GRID, getGeoHashBucketAgg } from './geo_hash'; export { tileMapVisType } from './tile_map_vis_type'; export { createTileMapFn } from './tile_map_fn'; export { tileMapRenderer } from './tile_map_renderer'; diff --git a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/utils.ts b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/utils.ts index 114c44b1c057d..9f0198c7ec0c7 100644 --- a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/utils.ts +++ b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/utils.ts @@ -9,6 +9,7 @@ import { Vis } from '@kbn/visualizations-plugin/public'; import { indexPatterns } from '@kbn/data-plugin/public'; import { TileMapVisParams } from './types'; import { title } from './tile_map_vis_type'; +import { GEOHASH_GRID } from './geo_hash'; export function extractLayerDescriptorParams(vis: Vis) { const params: { [key: string]: any } = { @@ -20,7 +21,7 @@ export function extractLayerDescriptorParams(vis: Vis) { }; const bucketAggs = vis.data?.aggs?.byType('buckets'); - if (bucketAggs?.length && bucketAggs[0].type.dslName === 'geohash_grid') { + if (bucketAggs?.length && bucketAggs[0].type.dslName === GEOHASH_GRID) { params.geoFieldName = bucketAggs[0].getField()?.name; } else if (vis.data.indexPattern) { // attempt to default to first geo point field when geohash is not configured yet diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 9890676f2cec5..a59d31c6b430b 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -29,7 +29,7 @@ import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/pu import { CONTEXT_MENU_TRIGGER } from '@kbn/embeddable-plugin/public'; import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import type { MapsEmsPluginPublicStart } from '@kbn/maps-ems-plugin/public'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { LicensingPluginSetup, LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { FileUploadPluginStart } from '@kbn/file-upload-plugin/public'; @@ -44,6 +44,8 @@ import type { LensPublicSetup } from '@kbn/lens-plugin/public'; import { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/public'; import { createRegionMapFn, + GEOHASH_GRID, + getGeoHashBucketAgg, regionMapRenderer, regionMapVisType, createTileMapFn, @@ -81,6 +83,7 @@ import { setupLensChoroplethChart } from './lens'; export interface MapsPluginSetupDependencies { cloud?: CloudSetup; + data: DataPublicPluginSetup; expressions: ReturnType; inspector: InspectorSetupContract; home?: HomePublicPluginSetup; @@ -201,6 +204,7 @@ export class MapsPlugin setupLensChoroplethChart(core, plugins.expressions, plugins.lens); // register wrapper around legacy tile_map and region_map visualizations + plugins.data.search.aggs.types.registerLegacy(GEOHASH_GRID, getGeoHashBucketAgg); plugins.expressions.registerFunction(createRegionMapFn); plugins.expressions.registerRenderer(regionMapRenderer); plugins.visualizations.createBaseVisualization(regionMapVisType); diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json index 8d32c7229a4e0..00cb20bc9e8e8 100644 --- a/x-pack/plugins/maps/tsconfig.json +++ b/x-pack/plugins/maps/tsconfig.json @@ -64,6 +64,7 @@ "@kbn/config-schema", "@kbn/controls-plugin", "@kbn/shared-ux-router", + "@kbn/field-types", ], "exclude": [ "target/**/*", diff --git a/x-pack/test/functional/apps/maps/group4/index.js b/x-pack/test/functional/apps/maps/group4/index.js index 70cb5ac660768..6af1369928d98 100644 --- a/x-pack/test/functional/apps/maps/group4/index.js +++ b/x-pack/test/functional/apps/maps/group4/index.js @@ -71,5 +71,6 @@ export default function ({ loadTestFile, getService }) { loadTestFile(require.resolve('./discover')); loadTestFile(require.resolve('./geofile_wizard_auto_open')); loadTestFile(require.resolve('./lens')); + loadTestFile(require.resolve('./tile_map')); }); } diff --git a/x-pack/test/functional/apps/maps/group4/tile_map.ts b/x-pack/test/functional/apps/maps/group4/tile_map.ts new file mode 100644 index 0000000000000..27cd25eb9893d --- /dev/null +++ b/x-pack/test/functional/apps/maps/group4/tile_map.ts @@ -0,0 +1,35 @@ +/* + * 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 default function ({ getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'visualize', 'lens', 'maps', 'timePicker']); + + describe('tile_map visualization', () => { + before(async () => { + await PageObjects.common.navigateToApp('visualize'); + await PageObjects.visualize.loadSavedVisualization('Visualization TileMap', { + navigateToVisualize: false, + }); + await PageObjects.timePicker.setAbsoluteRange( + PageObjects.timePicker.defaultStartTime, + PageObjects.timePicker.defaultEndTime + ); + await PageObjects.maps.waitForLayersToLoad(); + }); + + it('should render tile_map with map embeddable', async () => { + await PageObjects.maps.openLegend(); + await PageObjects.maps.waitForLayersToLoad(); + + expect(await PageObjects.maps.getNumberOfLayers()).to.eql(2); + expect(await PageObjects.maps.doesLayerExist('Visualization TileMap')).to.be(true); + }); + }); +} diff --git a/x-pack/test/functional/fixtures/kbn_archiver/maps.json b/x-pack/test/functional/fixtures/kbn_archiver/maps.json index 92507529a1b8a..6094688bee8aa 100644 --- a/x-pack/test/functional/fixtures/kbn_archiver/maps.json +++ b/x-pack/test/functional/fixtures/kbn_archiver/maps.json @@ -149,6 +149,33 @@ "version": "WzM2LDJd" } +{ + "attributes": { + "description": "TileMap", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "title": "Visualization TileMap", + "uiStateJSON": "{}", + "version": 1, + "visState": "{\"title\":\"New Visualization\",\"type\":\"tile_map\",\"params\":{\"mapType\":\"Scaled Circle Markers\",\"isDesaturated\":true,\"addTooltip\":true,\"heatMaxZoom\":16,\"heatMinOpacity\":0.1,\"heatRadius\":25,\"heatBlur\":15,\"heatNormalizeData\":true,\"wms\":{\"enabled\":false,\"url\":\"https://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer\",\"options\":{\"version\":\"1.3.0\",\"layers\":\"0\",\"format\":\"image/png\",\"transparent\":true,\"attribution\":\"Maps provided by USGS\",\"styles\":\"\"}}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"geohash_grid\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.coordinates\",\"autoPrecision\":true,\"precision\":2}}],\"listeners\":{}}" + }, + "coreMigrationVersion": "8.4.0", + "id": "Visualization-TileMap", + "migrationVersion": { + "visualization": "8.3.0" + }, + "references": [ + { + "id": "c698b940-e149-11e8-a35a-370a8516603a", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "version": "WzE1LDJd" +} + { "attributes": { "description": "",