Skip to content

Commit

Permalink
[maps] fix EMS vector file manifest is loaded in Discover (#189550)
Browse files Browse the repository at this point in the history
Closes #157429

Choropleth lens chart `registerVisualization` is called during Discover
page load. This PR removes loading of EMS files from
`registerVisualization` to avoid loading EMS files on Discover page
load.

There is a trade off since EMS files are needed in synchronous functions
such as `getSuggestions`. Before EMS files are loaded, getSuggestions
will return no suggestions.

### Test instructions
1. Load discover page with network tab open. Verify no requests are made
to `vector.maps.elastic.co`
2. Create a new lens visualization. Drag `geo.src` field from sample web
logs into the center. Verify region map suggestion is displayed. Click
it. Verify you can edit region key field.
<img width="600" alt="Screenshot 2024-07-30 at 12 04 58 PM"
src="https://github.com/user-attachments/assets/67d74c69-1f45-4c7a-a522-23735008c6dd">

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
nreese and elasticmachine authored Aug 5, 2024
1 parent f5047b7 commit 5267f4a
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,81 @@
* 2.0.
*/

import React from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui';
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow, EuiSelect } from '@elastic/eui';
import type { FileLayer } from '@elastic/ems-client';
import { ChoroplethChartState } from './types';
import { EMSFileSelect } from '../../components/ems_file_select';
import { getEmsFileLayers } from '../../util';

interface Props {
emsFileLayers: FileLayer[];
state: ChoroplethChartState;
setState: (state: ChoroplethChartState) => void;
}

export function RegionKeyEditor(props: Props) {
const [emsFileLayers, setEmsFileLayers] = useState<FileLayer[]>([]);
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
let ignore = false;
setIsLoading(true);
getEmsFileLayers()
.then((fileLayers) => {
if (!ignore) {
setEmsFileLayers(fileLayers);
setIsLoading(false);
}
})
.catch(() => {
if (!ignore) {
// eslint-disable-next-line no-console
console.warn(
`Lens region map is unable to access administrative boundaries from Elastic Maps Service (EMS). To avoid unnecessary EMS requests, set 'map.includeElasticMapsService: false' in 'kibana.yml'.`
);
setIsLoading(false);
}
});

return () => {
ignore = true;
};
}, []);

function onEmsLayerSelect(emsLayerId: string) {
const emsFields = getEmsFields(props.emsFileLayers, emsLayerId);
const emsFields = getEmsFields(emsFileLayers, emsLayerId);
props.setState({
...props.state,
emsLayerId,
emsField: emsFields.length ? emsFields[0].value : undefined,
});
}

function onEmsFieldSelect(selectedOptions: Array<EuiComboBoxOptionOption<string>>) {
if (selectedOptions.length === 0) {
return;
const emsFieldSelect = useMemo(() => {
const emsFields = getEmsFields(emsFileLayers, props.state.emsLayerId);
if (emsFields.length === 0) {
return null;
}

props.setState({
...props.state,
emsField: selectedOptions[0].value,
});
}
const selectedOption = props.state.emsField
? emsFields.find((option: EuiComboBoxOptionOption<string>) => {
return props.state.emsField === option.value;
})
: undefined;

function onEmsFieldSelect(selectedOptions: Array<EuiComboBoxOptionOption<string>>) {
if (selectedOptions.length === 0) {
return;
}

let emsFieldSelect;
const emsFields = getEmsFields(props.emsFileLayers, props.state.emsLayerId);
if (emsFields.length) {
let selectedOption;
if (props.state.emsField) {
selectedOption = emsFields.find((option: EuiComboBoxOptionOption<string>) => {
return props.state.emsField === option.value;
props.setState({
...props.state,
emsField: selectedOptions[0].value,
});
}
emsFieldSelect = (

return (
<EuiFormRow
label={i18n.translate('xpack.maps.choropleth.joinFieldLabel', {
defaultMessage: 'Join field',
Expand All @@ -64,8 +95,11 @@ export function RegionKeyEditor(props: Props) {
/>
</EuiFormRow>
);
}
return (
}, [emsFileLayers, props]);

return isLoading ? (
<EuiSelect isLoading />
) : (
<>
<EMSFileSelect
isColumnCompressed
Expand Down
13 changes: 0 additions & 13 deletions x-pack/plugins/maps/public/lens/choropleth_chart/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import type { ExpressionsSetup } from '@kbn/expressions-plugin/public';
import type { CoreSetup, CoreStart } from '@kbn/core/public';
import type { LensPublicSetup } from '@kbn/lens-plugin/public';
import type { FileLayer } from '@elastic/ems-client';
import type { MapsPluginStartDependencies } from '../../plugin';
import { getExpressionFunction } from './expression_function';
import { getExpressionRenderer } from './expression_renderer';
Expand All @@ -27,22 +26,10 @@ export function setupLensChoroplethChart(
lens.registerVisualization(async () => {
const [coreStart, plugins]: [CoreStart, MapsPluginStartDependencies, unknown] =
await coreSetup.getStartServices();
const { getEmsFileLayers } = await import('../../util');
const { getVisualization } = await import('./visualization');

let emsFileLayers: FileLayer[] = [];
try {
emsFileLayers = await getEmsFileLayers();
} catch (error) {
// eslint-disable-next-line no-console
console.warn(
`Lens region map setup is unable to access administrative boundaries from Elastic Maps Service (EMS). To avoid unnecessary EMS requests, set 'map.includeElasticMapsService: false' in 'kibana.yml'. For more details please visit ${coreStart.docLinks.links.maps.connectToEms}`
);
}

return getVisualization({
theme: coreStart.theme,
emsFileLayers,
paletteService: await plugins.charts.palettes.getPalettes(),
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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 type { SuggestionRequest, VisualizationSuggestion } from '@kbn/lens-plugin/public';
import type { FileLayer } from '@elastic/ems-client';
import type { ChoroplethChartState } from './types';

let emsFileLayers: FileLayer[] | undefined;
let getSuggestionsActual:
| ((
suggestionRequest: SuggestionRequest<ChoroplethChartState>,
emsFileLayers: FileLayer[]
) => Array<VisualizationSuggestion<ChoroplethChartState>>)
| undefined;
let promise: undefined | Promise<void>;

/**
* Avoid loading file layers during plugin setup
* Instead, load file layers when getSuggestions is called
* Since getSuggestions is sync, the trade off is that
* getSuggestions will return no suggestions until file layers load
*/
export function getSuggestionsLazy(
suggestionRequest: SuggestionRequest<ChoroplethChartState>
): Array<VisualizationSuggestion<ChoroplethChartState>> {
if (!promise) {
promise = new Promise((resolve, reject) => {
Promise.all([import('./suggestions'), import('../../util')])
.then(async ([{ getSuggestions }, { getEmsFileLayers }]) => {
getSuggestionsActual = getSuggestions;
try {
emsFileLayers = await getEmsFileLayers();
} catch (error) {
// eslint-disable-next-line no-console
console.warn(
`Lens region map is unable to access administrative boundaries from Elastic Maps Service (EMS). To avoid unnecessary EMS requests, set 'map.includeElasticMapsService: false' in 'kibana.yml'.`
);
}
resolve();
})
.catch(reject);
});
return [];
}

return emsFileLayers && getSuggestionsActual
? getSuggestionsActual(suggestionRequest, emsFileLayers)
: [];
}
23 changes: 10 additions & 13 deletions x-pack/plugins/maps/public/lens/choropleth_chart/visualization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@

import React from 'react';
import { i18n } from '@kbn/i18n';
import type { FileLayer } from '@elastic/ems-client';
import { dynamic } from '@kbn/shared-ux-utility';
import type { PaletteRegistry } from '@kbn/coloring';
import { ThemeServiceStart } from '@kbn/core/public';
import { layerTypes } from '@kbn/lens-plugin/public';
import type { OperationMetadata, SuggestionRequest, Visualization } from '@kbn/lens-plugin/public';
import { IconRegionMap } from '@kbn/chart-icons';
import { getSuggestions } from './suggestions';
import { getSuggestionsLazy } from './suggestions_lazy';
import type { ChoroplethChartState } from './types';
import { RegionKeyEditor } from './region_key_editor';

const REGION_KEY_GROUP_ID = 'region_key';
const METRIC_GROUP_ID = 'metric';
Expand All @@ -27,11 +26,9 @@ const CHART_LABEL = i18n.translate('xpack.maps.lens.choropleth.label', {
export const getVisualization = ({
paletteService,
theme,
emsFileLayers,
}: {
paletteService: PaletteRegistry;
theme: ThemeServiceStart;
emsFileLayers: FileLayer[];
}): Visualization<ChoroplethChartState> => ({
id: 'lnsChoropleth',

Expand Down Expand Up @@ -73,7 +70,7 @@ export const getVisualization = ({
},

getSuggestions(suggestionRequest: SuggestionRequest<ChoroplethChartState>) {
return getSuggestions(suggestionRequest, emsFileLayers);
return getSuggestionsLazy(suggestionRequest);
},

initialize(addNewLayer, state) {
Expand Down Expand Up @@ -194,13 +191,13 @@ export const getVisualization = ({

DimensionEditorComponent(props) {
if (props.groupId === REGION_KEY_GROUP_ID) {
return (
<RegionKeyEditor
emsFileLayers={emsFileLayers}
state={props.state}
setState={props.setState}
/>
);
const DimensionEditor = dynamic(async () => {
const { RegionKeyEditor } = await import('./region_key_editor');
return {
default: RegionKeyEditor,
};
});
return <DimensionEditor state={props.state} setState={props.setState} />;
}
return null;
},
Expand Down

0 comments on commit 5267f4a

Please sign in to comment.