diff --git a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap index f65b08fc632bc..0c60d3b0606be 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap +++ b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap @@ -204,7 +204,7 @@ Array [ class="euiSpacer euiSpacer--s emotion-euiSpacer-s" />,
@@ -255,7 +255,7 @@ Array [
@@ -546,7 +546,7 @@ Array [ class="euiSpacer euiSpacer--s emotion-euiSpacer-s" />,
@@ -597,7 +597,7 @@ Array [
diff --git a/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx b/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx index 307c074ce5c6f..3deee5fe0bda9 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx +++ b/src/plugins/data/public/utils/table_inspector_view/components/data_table.tsx @@ -8,7 +8,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; - +import { css } from '@emotion/react'; import { EuiButtonIcon, EuiFlexGroup, @@ -192,12 +192,25 @@ export class DataTableFormat extends Component div:last-child { + position: sticky; + left: 0; + } + `} /> ); } diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx index 6a53ae14186de..fddf14c864743 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx @@ -59,6 +59,9 @@ const currentDocumentSelector = (state: PreviewState) => state.documents[state.c const currentDocumentIsLoadingSelector = (state: PreviewState) => state.isLoadingDocuments; const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Props) => { + const { + validation: { setScriptEditorValidation }, + } = useFieldPreviewContext(); const monacoEditor = useRef(null); const editorValidationSubscription = useRef(); const fieldCurrentValue = useRef(''); @@ -143,7 +146,7 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr editorValidationSubscription.current = PainlessLang.validation$().subscribe( ({ isValid, isValidating, errors }) => { - controller.setScriptEditorValidation({ + setScriptEditorValidation({ isValid, isValidating, message: errors[0]?.message ?? null, @@ -151,7 +154,7 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr } ); }, - [controller] + [setScriptEditorValidation] ); const updateMonacoMarkers = useCallback((markers: monaco.editor.IMarkerData[]) => { diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx index 3addd448f1e7e..f554025ce9f4b 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx @@ -75,8 +75,6 @@ const documentsSelector = (state: PreviewState) => { }; }; -const scriptEditorValidationSelector = (state: PreviewState) => state.scriptEditorValidation; - export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewController }> = ({ controller, children, @@ -121,6 +119,12 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro /** The parameters required for the Painless _execute API */ const [params, setParams] = useState(defaultParams); + const [scriptEditorValidation, setScriptEditorValidation] = useState<{ + isValidating: boolean; + isValid: boolean; + message: string | null; + }>({ isValidating: false, isValid: true, message: null }); + /** Flag to show/hide the preview panel */ const [isPanelVisible, setIsPanelVisible] = useState(true); /** Flag to indicate if we are loading document from cluster */ @@ -133,10 +137,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const { currentDocument, currentDocIndex, currentDocId, totalDocs, currentIdx } = useStateSelector(controller.state$, documentsSelector); - const scriptEditorValidation = useStateSelector( - controller.state$, - scriptEditorValidationSelector - ); let isPreviewAvailable = true; @@ -513,6 +513,9 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro isVisible: isPanelVisible, setIsVisible: setIsPanelVisible, }, + validation: { + setScriptEditorValidation, + }, reset, }), [ diff --git a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts index 80b11e74597aa..b572827eac06d 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts @@ -95,9 +95,11 @@ export class PreviewController { } }; + /* disabled while investigating issues with painless script editor setScriptEditorValidation = (scriptEditorValidation: PreviewState['scriptEditorValidation']) => { this.updateState({ scriptEditorValidation }); }; + */ setCustomId = (customId?: string) => { this.updateState({ customId }); diff --git a/src/plugins/data_view_field_editor/public/components/preview/types.ts b/src/plugins/data_view_field_editor/public/components/preview/types.ts index 377aed627ba54..347e0a709cf28 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/types.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/types.ts @@ -133,6 +133,11 @@ export interface Context { isLastDoc: boolean; }; reset: () => void; + validation: { + setScriptEditorValidation: React.Dispatch< + React.SetStateAction<{ isValid: boolean; isValidating: boolean; message: string | null }> + >; + }; } export type PainlessExecuteContext = diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index c4dab3f156caa..6c8f719bf2886 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -15,6 +15,10 @@ export const APP_ICON_SOLUTION = 'logoKibana'; export const APP_NAME = i18n.translate('xpack.maps.visTypeAlias.title', { defaultMessage: 'Maps', }); +export const MAP_EMBEDDABLE_NAME = i18n.translate('xpack.maps.embeddableDisplayName', { + defaultMessage: 'map', +}); + export const INITIAL_LAYERS_KEY = 'initialLayers'; export const MAPS_APP_PATH = `app/${APP_ID}`; @@ -36,6 +40,8 @@ export const OPEN_LAYER_WIZARD = 'openLayerWizard'; // Centroids are a single point for representing lines, multiLines, polygons, and multiPolygons export const KBN_IS_CENTROID_FEATURE = '__kbn_is_centroid_feature__'; +export const GEOJSON_FEATURE_ID_PROPERTY_NAME = '__kbn__feature_id__'; + export function getNewMapPath() { return `/${MAPS_APP_PATH}/${MAP_PATH}`; } diff --git a/x-pack/plugins/maps/common/i18n_getters.ts b/x-pack/plugins/maps/common/i18n_getters.ts index 0c59cc891504e..09df14ef9f289 100644 --- a/x-pack/plugins/maps/common/i18n_getters.ts +++ b/x-pack/plugins/maps/common/i18n_getters.ts @@ -9,18 +9,6 @@ import { i18n } from '@kbn/i18n'; import { ES_SPATIAL_RELATIONS } from './constants'; -export function getAppTitle() { - return i18n.translate('xpack.maps.appTitle', { - defaultMessage: 'Maps', - }); -} - -export function getMapEmbeddableDisplayName() { - return i18n.translate('xpack.maps.embeddableDisplayName', { - defaultMessage: 'map', - }); -} - export function getDataSourceLabel() { return i18n.translate('xpack.maps.source.dataSourceLabel', { defaultMessage: 'Data source', diff --git a/x-pack/plugins/maps/public/api/create_layer_descriptors.ts b/x-pack/plugins/maps/public/api/create_layer_descriptors.ts index cfc914ef7c0f4..553abcf7d5481 100644 --- a/x-pack/plugins/maps/public/api/create_layer_descriptors.ts +++ b/x-pack/plugins/maps/public/api/create_layer_descriptors.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { LayerDescriptor } from '../../common/descriptor_types'; -import { lazyLoadMapModules } from '../lazy_load_bundle'; +import type { LayerDescriptor } from '../../common/descriptor_types'; import type { CreateLayerDescriptorParams } from '../classes/sources/es_search_source'; export const createLayerDescriptors = { @@ -14,17 +13,21 @@ export const createLayerDescriptors = { indexPatternId: string, indexPatternTitle: string ): Promise { - const mapModules = await lazyLoadMapModules(); - return mapModules.createSecurityLayerDescriptors(indexPatternId, indexPatternTitle); + const { createSecurityLayerDescriptors } = await import( + '../classes/layers/wizards/solution_layers/security' + ); + return createSecurityLayerDescriptors(indexPatternId, indexPatternTitle); }, async createBasemapLayerDescriptor(): Promise { - const mapModules = await lazyLoadMapModules(); - return mapModules.createBasemapLayerDescriptor(); + const { createBasemapLayerDescriptor } = await import( + '../classes/layers/create_basemap_layer_descriptor' + ); + return createBasemapLayerDescriptor(); }, async createESSearchSourceLayerDescriptor( params: CreateLayerDescriptorParams ): Promise { - const mapModules = await lazyLoadMapModules(); - return mapModules.createESSearchSourceLayerDescriptor(params); + const { createLayerDescriptor } = await import('../classes/sources/es_search_source'); + return createLayerDescriptor(params); }, }; diff --git a/x-pack/plugins/maps/public/api/ems.ts b/x-pack/plugins/maps/public/api/ems.ts index da6e88c84e22c..64395bddefab4 100644 --- a/x-pack/plugins/maps/public/api/ems.ts +++ b/x-pack/plugins/maps/public/api/ems.ts @@ -5,12 +5,11 @@ * 2.0. */ -import { EMSTermJoinConfig, SampleValuesConfig } from '../ems_autosuggest'; -import { lazyLoadMapModules } from '../lazy_load_bundle'; +import type { EMSTermJoinConfig, SampleValuesConfig } from '../ems_autosuggest'; export async function suggestEMSTermJoinConfig( sampleValuesConfig: SampleValuesConfig ): Promise { - const mapModules = await lazyLoadMapModules(); - return await mapModules.suggestEMSTermJoinConfig(sampleValuesConfig); + const { suggestEMSTermJoinConfig: suggestEms } = await import('../ems_autosuggest'); + return await suggestEms(sampleValuesConfig); } diff --git a/x-pack/plugins/maps/public/classes/layers/index.ts b/x-pack/plugins/maps/public/classes/layers/index.ts index b068d4d234170..fe7e297ba8a1d 100644 --- a/x-pack/plugins/maps/public/classes/layers/index.ts +++ b/x-pack/plugins/maps/public/classes/layers/index.ts @@ -6,4 +6,4 @@ */ export type { LayerWizard, LayerWizardWithMeta, RenderWizardArguments } from './wizards'; -export { getLayerWizards, registerLayerWizardExternal } from './wizards'; +export { getLayerWizards } from './wizards'; diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.test.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.test.ts index 2250e86da0ec2..d4a34c9104a2e 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.test.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { assignFeatureIds, GEOJSON_FEATURE_ID_PROPERTY_NAME } from './assign_feature_ids'; +import { GEOJSON_FEATURE_ID_PROPERTY_NAME } from '../../../../../common/constants'; +import { assignFeatureIds } from './assign_feature_ids'; import { FeatureCollection, Feature, Point } from 'geojson'; const featureId = 'myFeature1'; diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.ts index 3611256d246fb..f4b39c7a0d324 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids.ts @@ -6,9 +6,8 @@ */ import _ from 'lodash'; -import { FeatureCollection, Feature } from 'geojson'; - -export const GEOJSON_FEATURE_ID_PROPERTY_NAME = '__kbn__feature_id__'; +import type { FeatureCollection, Feature } from 'geojson'; +import { GEOJSON_FEATURE_ID_PROPERTY_NAME } from '../../../../../common/constants'; let idCounter = 0; diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx index 419757e24633c..a337cdbfaa7a5 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx @@ -14,6 +14,7 @@ import type { FilterSpecification, Map as MbMap, GeoJSONSource } from '@kbn/mapb import { EMPTY_FEATURE_COLLECTION, FEATURE_VISIBLE_PROPERTY_NAME, + GEOJSON_FEATURE_ID_PROPERTY_NAME, LAYER_TYPE, SOURCE_BOUNDS_DATA_REQUEST_ID, } from '../../../../../common/constants'; @@ -35,7 +36,6 @@ import { } from '../vector_layer'; import { DataRequestAbortError } from '../../../util/data_request'; import { getFeatureCollectionBounds } from '../../../util/get_feature_collection_bounds'; -import { GEOJSON_FEATURE_ID_PROPERTY_NAME } from './assign_feature_ids'; import { syncGeojsonSourceData } from './geojson_source_data'; import { performInnerJoins } from './perform_inner_joins'; import { pluckStyleMetaFromFeatures } from './pluck_style_meta_from_features'; diff --git a/x-pack/plugins/maps/public/classes/layers/wizards/index.ts b/x-pack/plugins/maps/public/classes/layers/wizards/index.ts index 814a2ec8e5c2f..ecb690b3d1f15 100644 --- a/x-pack/plugins/maps/public/classes/layers/wizards/index.ts +++ b/x-pack/plugins/maps/public/classes/layers/wizards/index.ts @@ -10,4 +10,4 @@ export type { LayerWizardWithMeta, RenderWizardArguments, } from './layer_wizard_registry'; -export { getLayerWizards, registerLayerWizardExternal } from './layer_wizard_registry'; +export { getLayerWizards } from './layer_wizard_registry'; diff --git a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx index e11b1a0530466..fa77048f6b88e 100644 --- a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import '../../_index.scss'; import React, { Component } from 'react'; import classNames from 'classnames'; import { EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui'; diff --git a/x-pack/plugins/maps/public/embeddable/map_component.tsx b/x-pack/plugins/maps/public/embeddable/map_component.tsx index 13a589ebd0946..2e080a46d8478 100644 --- a/x-pack/plugins/maps/public/embeddable/map_component.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_component.tsx @@ -8,25 +8,19 @@ import React, { Component, RefObject } from 'react'; import { first } from 'rxjs/operators'; import { v4 as uuidv4 } from 'uuid'; -import { EuiLoadingChart } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; import type { Query, TimeRange } from '@kbn/es-query'; import type { LayerDescriptor, MapCenterAndZoom } from '../../common/descriptor_types'; import type { MapEmbeddableType } from './types'; -import type { LazyLoadedMapModules } from '../lazy_load_bundle'; -import { lazyLoadMapModules } from '../lazy_load_bundle'; +import { MapEmbeddable } from './map_embeddable'; +import { createBasemapLayerDescriptor } from '../classes/layers/create_basemap_layer_descriptor'; interface Props { title: string; filters?: Filter[]; query?: Query; timeRange?: TimeRange; - getLayerDescriptors: ( - mapModules: Pick< - LazyLoadedMapModules, - 'createTileMapLayerDescriptor' | 'createRegionMapLayerDescriptor' - > - ) => LayerDescriptor[]; + getLayerDescriptors: () => LayerDescriptor[]; mapCenter?: MapCenterAndZoom; onInitialRenderComplete?: () => void; /* @@ -35,48 +29,13 @@ interface Props { isSharable?: boolean; } -interface State { - isLoaded: boolean; -} - -export class MapComponent extends Component { - private _isMounted = false; - private _mapEmbeddable?: MapEmbeddableType | undefined; +export class MapComponent extends Component { + private _mapEmbeddable: MapEmbeddableType; private readonly _embeddableRef: RefObject = React.createRef(); - state: State = { isLoaded: false }; - - componentDidMount() { - this._isMounted = true; - this._load(); - } - - componentWillUnmount() { - this._isMounted = false; - if (this._mapEmbeddable) { - this._mapEmbeddable.destroy(); - } - } - - componentDidUpdate() { - if (this._mapEmbeddable) { - this._mapEmbeddable.updateInput({ - filters: this.props.filters, - query: this.props.query, - timeRange: this.props.timeRange, - }); - } - } - - async _load() { - const mapModules = await lazyLoadMapModules(); - if (!this._isMounted) { - return; - } - - this.setState({ isLoaded: true }); - - this._mapEmbeddable = new mapModules.MapEmbeddable( + constructor(props: Props) { + super(props); + this._mapEmbeddable = new MapEmbeddable( { editable: false, }, @@ -85,11 +44,8 @@ export class MapComponent extends Component { attributes: { title: this.props.title, layerListJSON: JSON.stringify([ - mapModules.createBasemapLayerDescriptor(), - ...this.props.getLayerDescriptors({ - createRegionMapLayerDescriptor: mapModules.createRegionMapLayerDescriptor, - createTileMapLayerDescriptor: mapModules.createTileMapLayerDescriptor, - }), + createBasemapLayerDescriptor(), + ...this.props.getLayerDescriptors(), ]), }, mapCenter: this.props.mapCenter, @@ -101,7 +57,7 @@ export class MapComponent extends Component { .getOnRenderComplete$() .pipe(first()) .subscribe(() => { - if (this._isMounted && this.props.onInitialRenderComplete) { + if (this.props.onInitialRenderComplete) { this.props.onInitialRenderComplete(); } }); @@ -110,16 +66,27 @@ export class MapComponent extends Component { if (this.props.isSharable !== undefined) { this._mapEmbeddable.setIsSharable(this.props.isSharable); } + } + + componentDidMount() { if (this._embeddableRef.current) { this._mapEmbeddable.render(this._embeddableRef.current); } } - render() { - if (!this.state.isLoaded) { - return ; - } + componentWillUnmount() { + this._mapEmbeddable.destroy(); + } + componentDidUpdate() { + this._mapEmbeddable.updateInput({ + filters: this.props.filters, + query: this.props.query, + timeRange: this.props.timeRange, + }); + } + + render() { return
; } } diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index 33552613c0c95..834023182f45b 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -21,6 +21,7 @@ import { startWith, } from 'rxjs/operators'; import { Unsubscribe } from 'redux'; +import type { PaletteRegistry } from '@kbn/coloring'; import type { KibanaExecutionContext } from '@kbn/core/public'; import { EuiEmptyPrompt } from '@elastic/eui'; import { type Filter } from '@kbn/es-query'; @@ -82,7 +83,7 @@ import { } from '../../common/constants'; import { RenderToolTipContent } from '../classes/tooltips/tooltip_property'; import { - getChartsPaletteServiceGetColor, + getCharts, getCoreI18n, getExecutionContextService, getHttp, @@ -109,6 +110,24 @@ import { MapEmbeddableOutput, } from './types'; +async function getChartsPaletteServiceGetColor(): Promise<((value: string) => string) | null> { + const chartsService = getCharts(); + const paletteRegistry: PaletteRegistry | null = chartsService + ? await chartsService.palettes.getPalettes() + : null; + if (!paletteRegistry) { + return null; + } + + const paletteDefinition = paletteRegistry.get('default'); + const chartConfiguration = { syncColors: true }; + return (value: string) => { + const series = [{ name: value, rankAtDepth: 0, totalSeriesAtDepth: 1 }]; + const color = paletteDefinition.getCategoricalColor(series, chartConfiguration); + return color ? color : '#3d3d3d'; + }; +} + function getIsRestore(searchSessionId?: string) { if (!searchSessionId) { return false; diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts index 3642448774c58..ae9d4b6d39329 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -8,12 +8,10 @@ import { first } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; import { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; -import { MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants'; -import { getMapEmbeddableDisplayName } from '../../common/i18n_getters'; +import { MAP_SAVED_OBJECT_TYPE, APP_ICON, MAP_EMBEDDABLE_NAME } from '../../common/constants'; import { extract, inject } from '../../common/embeddable'; import { MapByReferenceInput, MapEmbeddableInput } from './types'; -import { lazyLoadMapModules } from '../lazy_load_bundle'; -import { getApplication, getUsageCollection } from '../kibana_services'; +import { getApplication, getMapsCapabilities, getUsageCollection } from '../kibana_services'; export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { type = MAP_SAVED_OBJECT_TYPE; @@ -26,7 +24,6 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { }; async isEditable() { - const { getMapsCapabilities } = await lazyLoadMapModules(); return getMapsCapabilities().save as boolean; } @@ -36,7 +33,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { } getDisplayName() { - return getMapEmbeddableDisplayName(); + return MAP_EMBEDDABLE_NAME; } createFromSavedObject = async ( @@ -51,7 +48,7 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { }; create = async (input: MapEmbeddableInput, parent?: IContainer) => { - const { MapEmbeddable } = await lazyLoadMapModules(); + const { MapEmbeddable } = await import('./map_embeddable'); const usageCollection = getUsageCollection(); if (usageCollection) { // currentAppId$ is a BehaviorSubject exposed as an observable so subscription gets last value upon subscribe diff --git a/x-pack/plugins/maps/public/feature_catalogue_entry.ts b/x-pack/plugins/maps/public/feature_catalogue_entry.ts index 80d37aa0288e7..b897795e2eb49 100644 --- a/x-pack/plugins/maps/public/feature_catalogue_entry.ts +++ b/x-pack/plugins/maps/public/feature_catalogue_entry.ts @@ -7,12 +7,11 @@ import { i18n } from '@kbn/i18n'; import type { FeatureCatalogueCategory } from '@kbn/home-plugin/public'; -import { APP_ID, APP_ICON } from '../common/constants'; -import { getAppTitle } from '../common/i18n_getters'; +import { APP_ID, APP_ICON, APP_NAME } from '../common/constants'; export const featureCatalogueEntry = { id: APP_ID, - title: getAppTitle(), + title: APP_NAME, subtitle: i18n.translate('xpack.maps.featureCatalogue.mapsSubtitle', { defaultMessage: 'Plot geographic data.', }), diff --git a/x-pack/plugins/maps/public/index.ts b/x-pack/plugins/maps/public/index.ts index beb0d5153d89e..e88a167a6da24 100644 --- a/x-pack/plugins/maps/public/index.ts +++ b/x-pack/plugins/maps/public/index.ts @@ -17,12 +17,10 @@ export const plugin: PluginInitializer = ( return new MapsPlugin(initContext); }; -export { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; -export { MAPS_APP_LOCATOR } from './locators'; +export { GEOJSON_FEATURE_ID_PROPERTY_NAME, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; +export { MAPS_APP_LOCATOR } from './locators/map_locator/locator_definition'; export type { PreIndexedShape } from '../common/elasticsearch_util'; -export { GEOJSON_FEATURE_ID_PROPERTY_NAME } from './classes/layers/vector_layer/geojson_vector_layer/assign_feature_ids'; - export type { ITooltipProperty, RenderTooltipContentParams, diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts index 315f75c313fa5..623f82a473472 100644 --- a/x-pack/plugins/maps/public/kibana_services.ts +++ b/x-pack/plugins/maps/public/kibana_services.ts @@ -6,7 +6,6 @@ */ import type { CoreStart } from '@kbn/core/public'; -import type { PaletteRegistry } from '@kbn/coloring'; import type { EMSSettings } from '@kbn/maps-ems-plugin/common/ems_settings'; import { MapsEmsPluginPublicStart } from '@kbn/maps-ems-plugin/public'; import type { MapsConfigType } from '../config'; @@ -50,6 +49,7 @@ export const getMapsCapabilities = () => coreStart.application.capabilities.maps export const getVisualizeCapabilities = () => coreStart.application.capabilities.visualize; export const getDocLinks = () => coreStart.docLinks; export const getCoreOverlays = () => coreStart.overlays; +export const getCharts = () => pluginsStart.charts; export const getData = () => pluginsStart.data; export const getUiActions = () => pluginsStart.uiActions; export const getCore = () => coreStart; @@ -90,34 +90,7 @@ export const getEMSSettings: () => EMSSettings = () => { export const getEmsTileLayerId = () => mapsEms.config.emsTileLayerId; -export const getTilemap = () => { - if (mapsEms.config.tilemap) { - return mapsEms.config.tilemap; - } else { - return {}; - } -}; - export const getShareService = () => pluginsStart.share; export const getIsAllowByValueEmbeddables = () => pluginsStart.dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables; - -export async function getChartsPaletteServiceGetColor(): Promise< - ((value: string) => string) | null -> { - const paletteRegistry: PaletteRegistry | null = pluginsStart.charts - ? await pluginsStart.charts.palettes.getPalettes() - : null; - if (!paletteRegistry) { - return null; - } - - const paletteDefinition = paletteRegistry.get('default'); - const chartConfiguration = { syncColors: true }; - return (value: string) => { - const series = [{ name: value, rankAtDepth: 0, totalSeriesAtDepth: 1 }]; - const color = paletteDefinition.getCategoricalColor(series, chartConfiguration); - return color ? color : '#3d3d3d'; - }; -} diff --git a/x-pack/plugins/maps/public/lazy_load_bundle/index.ts b/x-pack/plugins/maps/public/lazy_load_bundle/index.ts deleted file mode 100644 index a2f4f4004797e..0000000000000 --- a/x-pack/plugins/maps/public/lazy_load_bundle/index.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 { DataViewsContract } from '@kbn/data-views-plugin/common'; -import { AppMountParameters, CoreStart } from '@kbn/core/public'; -import { IContainer } from '@kbn/embeddable-plugin/public'; -import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; -import { LayerDescriptor } from '../../common/descriptor_types'; -import type { - MapEmbeddableConfig, - MapEmbeddableInput, - MapEmbeddableType, -} from '../embeddable/types'; -import type { CreateLayerDescriptorParams } from '../classes/sources/es_search_source'; -import type { EMSTermJoinConfig, SampleValuesConfig } from '../ems_autosuggest'; -import type { CreateTileMapLayerDescriptorParams } from '../classes/layers/create_tile_map_layer_descriptor'; -import type { CreateRegionMapLayerDescriptorParams } from '../classes/layers/create_region_map_layer_descriptor'; - -let loadModulesPromise: Promise; - -export interface LazyLoadedMapModules { - MapEmbeddable: new ( - config: MapEmbeddableConfig, - initialInput: MapEmbeddableInput, - parent?: IContainer - ) => MapEmbeddableType; - getIndexPatternService: () => DataViewsContract; - getMapsCapabilities: () => any; - renderApp: ( - params: AppMountParameters, - deps: { - coreStart: CoreStart; - AppUsageTracker: React.FC; - savedObjectsTagging?: SavedObjectTaggingPluginStart; - } - ) => Promise<() => void>; - createSecurityLayerDescriptors: ( - indexPatternId: string, - indexPatternTitle: string - ) => LayerDescriptor[]; - createTileMapLayerDescriptor: ({ - label, - mapType, - colorSchema, - indexPatternId, - geoFieldName, - metricAgg, - metricFieldName, - }: CreateTileMapLayerDescriptorParams) => LayerDescriptor | null; - createRegionMapLayerDescriptor: ({ - label, - emsLayerId, - leftFieldName, - termsFieldName, - termsSize, - colorSchema, - indexPatternId, - metricAgg, - metricFieldName, - }: CreateRegionMapLayerDescriptorParams) => LayerDescriptor | null; - createBasemapLayerDescriptor: () => LayerDescriptor | null; - createESSearchSourceLayerDescriptor: (params: CreateLayerDescriptorParams) => LayerDescriptor; - suggestEMSTermJoinConfig: (config: SampleValuesConfig) => Promise; -} - -export async function lazyLoadMapModules(): Promise { - if (typeof loadModulesPromise !== 'undefined') { - return loadModulesPromise; - } - - loadModulesPromise = new Promise(async (resolve, reject) => { - try { - const { - MapEmbeddable, - getIndexPatternService, - getMapsCapabilities, - renderApp, - createSecurityLayerDescriptors, - createTileMapLayerDescriptor, - createRegionMapLayerDescriptor, - createBasemapLayerDescriptor, - createESSearchSourceLayerDescriptor, - suggestEMSTermJoinConfig, - } = await import('./lazy'); - resolve({ - MapEmbeddable, - getIndexPatternService, - getMapsCapabilities, - renderApp, - createSecurityLayerDescriptors, - createTileMapLayerDescriptor, - createRegionMapLayerDescriptor, - createBasemapLayerDescriptor, - createESSearchSourceLayerDescriptor, - suggestEMSTermJoinConfig, - }); - } catch (error) { - reject(error); - } - }); - return loadModulesPromise; -} diff --git a/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts b/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts deleted file mode 100644 index fb5321dfc03f8..0000000000000 --- a/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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 '../../_index.scss'; -export * from '../../embeddable/map_embeddable'; -export * from '../../kibana_services'; -export { renderApp } from '../../render_app'; -export * from '../../classes/layers/wizards/solution_layers/security'; -export { createTileMapLayerDescriptor } from '../../classes/layers/create_tile_map_layer_descriptor'; -export { createRegionMapLayerDescriptor } from '../../classes/layers/create_region_map_layer_descriptor'; -export { createBasemapLayerDescriptor } from '../../classes/layers/create_basemap_layer_descriptor'; -export { createLayerDescriptor as createESSearchSourceLayerDescriptor } from '../../classes/sources/es_search_source'; -export { suggestEMSTermJoinConfig } from '../../ems_autosuggest'; diff --git a/x-pack/plugins/maps/public/legacy_visualizations/index.ts b/x-pack/plugins/maps/public/legacy_visualizations/index.ts index d99544bbb207d..177cc608a975c 100644 --- a/x-pack/plugins/maps/public/legacy_visualizations/index.ts +++ b/x-pack/plugins/maps/public/legacy_visualizations/index.ts @@ -8,4 +8,3 @@ 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/region_map/region_map_visualization.tsx b/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_visualization.tsx index b6945995da9d9..4dedb97202857 100644 --- a/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_visualization.tsx +++ b/x-pack/plugins/maps/public/legacy_visualizations/region_map/region_map_visualization.tsx @@ -9,8 +9,8 @@ import React from 'react'; import type { Filter } from '@kbn/es-query'; import type { Query, TimeRange } from '@kbn/es-query'; import { RegionMapVisConfig } from './types'; -import type { LazyLoadedMapModules } from '../../lazy_load_bundle'; import { MapComponent } from '../../embeddable/map_component'; +import { createRegionMapLayerDescriptor } from '../../classes/layers/create_region_map_layer_descriptor'; interface Props { filters?: Filter[]; @@ -26,11 +26,7 @@ function RegionMapVisualization(props: Props) { lon: props.visConfig.mapCenter[1], zoom: props.visConfig.mapZoom, }; - function getLayerDescriptors({ - createRegionMapLayerDescriptor, - }: { - createRegionMapLayerDescriptor: LazyLoadedMapModules['createRegionMapLayerDescriptor']; - }) { + function getLayerDescriptors() { const layerDescriptor = createRegionMapLayerDescriptor(props.visConfig.layerDescriptorParams); return layerDescriptor ? [layerDescriptor] : []; } diff --git a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_visualization.tsx b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_visualization.tsx index 97a3609d765a4..5eb4132528de5 100644 --- a/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_visualization.tsx +++ b/x-pack/plugins/maps/public/legacy_visualizations/tile_map/tile_map_visualization.tsx @@ -8,9 +8,9 @@ import React from 'react'; import type { Filter } from '@kbn/es-query'; import type { Query, TimeRange } from '@kbn/es-query'; -import { TileMapVisConfig } from './types'; -import type { LazyLoadedMapModules } from '../../lazy_load_bundle'; +import type { TileMapVisConfig } from './types'; import { MapComponent } from '../../embeddable/map_component'; +import { createTileMapLayerDescriptor } from '../../classes/layers/create_tile_map_layer_descriptor'; interface Props { filters?: Filter[]; @@ -26,11 +26,7 @@ function TileMapVisualization(props: Props) { lon: props.visConfig.mapCenter[1], zoom: props.visConfig.mapZoom, }; - function getLayerDescriptors({ - createTileMapLayerDescriptor, - }: { - createTileMapLayerDescriptor: LazyLoadedMapModules['createTileMapLayerDescriptor']; - }) { + function getLayerDescriptors() { const layerDescriptor = createTileMapLayerDescriptor(props.visConfig.layerDescriptorParams); return layerDescriptor ? [layerDescriptor] : []; } diff --git a/x-pack/plugins/maps/public/locators.ts b/x-pack/plugins/maps/public/locators.ts deleted file mode 100644 index ff2ac6590708e..0000000000000 --- a/x-pack/plugins/maps/public/locators.ts +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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. - */ - -/* eslint-disable max-classes-per-file */ - -import rison from '@kbn/rison'; -import type { DataViewSpec } from '@kbn/data-views-plugin/public'; -import type { SerializableRecord } from '@kbn/utility-types'; -import { type Filter, isFilterPinned, type TimeRange, type Query } from '@kbn/es-query'; -import type { GlobalQueryStateFromUrl, RefreshInterval } from '@kbn/data-plugin/public'; -import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; -import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; -import type { LayerDescriptor } from '../common/descriptor_types'; -import { INITIAL_LAYERS_KEY, APP_ID } from '../common/constants'; -import { lazyLoadMapModules } from './lazy_load_bundle'; - -export interface MapsAppLocatorParams extends SerializableRecord { - /** - * If given, it will load the given map else will load the create a new map page. - */ - mapId?: string; - - /** - * Optionally set the time range in the time picker. - */ - timeRange?: TimeRange; - - /** - * Optionally set the initial Layers. - */ - initialLayers?: LayerDescriptor[] & SerializableRecord; - - /** - * Optionally set the refresh interval. - */ - refreshInterval?: RefreshInterval & SerializableRecord; - - /** - * Optionally apply filers. NOTE: if given and used in conjunction with `mapId`, and the - * saved map has filters saved with it, this will _replace_ those filters. - */ - filters?: Filter[]; - - /** - * Optionally set a query. NOTE: if given and used in conjunction with `mapId`, and the - * saved map has a query saved with it, this will _replace_ that query. - */ - query?: Query; - - /** - * If not given, will use the uiSettings configuration for `storeInSessionStorage`. useHash determines - * whether to hash the data in the url to avoid url length issues. - */ - hash?: boolean; - - /** - * Optionally pass adhoc data view spec. - */ - dataViewSpec?: DataViewSpec; -} - -export const MAPS_APP_LOCATOR = 'MAPS_APP_LOCATOR' as const; - -export type MapsAppLocator = LocatorPublic; - -export interface MapsAppLocatorDependencies { - useHash: boolean; -} - -export class MapsAppLocatorDefinition implements LocatorDefinition { - public readonly id = MAPS_APP_LOCATOR; - - constructor(protected readonly deps: MapsAppLocatorDependencies) {} - - public readonly getLocation = async (params: MapsAppLocatorParams) => { - const { mapId, filters, query, refreshInterval, timeRange, initialLayers, hash } = params; - const useHash = hash ?? this.deps.useHash; - const appState: { - query?: Query; - filters?: Filter[]; - vis?: unknown; - } = {}; - const queryState: GlobalQueryStateFromUrl = {}; - - if (query) appState.query = query; - if (filters && filters.length) appState.filters = filters?.filter((f) => !isFilterPinned(f)); - if (timeRange) queryState.time = timeRange; - if (filters && filters.length) queryState.filters = filters?.filter((f) => isFilterPinned(f)); - if (refreshInterval) queryState.refreshInterval = refreshInterval; - - let path = `/map#/${mapId || ''}`; - path = setStateToKbnUrl('_g', queryState, { useHash }, path); - path = setStateToKbnUrl('_a', appState, { useHash }, path); - - if (initialLayers && initialLayers.length) { - const risonEncodedInitialLayers = rison.encodeArray(initialLayers); - path = `${path}&${INITIAL_LAYERS_KEY}=${encodeURIComponent(risonEncodedInitialLayers)}`; - } - - return { - app: APP_ID, - path, - state: params.dataViewSpec - ? { - dataViewSpec: params.dataViewSpec, - } - : {}, - }; - }; -} - -export interface MapsAppTileMapLocatorParams extends SerializableRecord { - label: string; - mapType: string; - colorSchema: string; - indexPatternId?: string; - geoFieldName?: string; - metricAgg: string; - metricFieldName?: string; - timeRange?: TimeRange; - filters?: Filter[]; - query?: Query; - hash?: boolean; -} - -export type MapsAppTileMapLocator = LocatorPublic; - -export const MAPS_APP_TILE_MAP_LOCATOR = 'MAPS_APP_TILE_MAP_LOCATOR' as const; - -export interface MapsAppTileMapLocatorDependencies { - locator: MapsAppLocator; -} - -export class MapsAppTileMapLocatorDefinition - implements LocatorDefinition -{ - public readonly id = MAPS_APP_TILE_MAP_LOCATOR; - - constructor(protected readonly deps: MapsAppTileMapLocatorDependencies) {} - - public readonly getLocation = async (params: MapsAppTileMapLocatorParams) => { - const { - label, - mapType, - colorSchema, - indexPatternId, - geoFieldName, - metricAgg, - metricFieldName, - filters, - query, - timeRange, - hash = true, - } = params; - const mapModules = await lazyLoadMapModules(); - const initialLayers = [] as unknown as LayerDescriptor[] & SerializableRecord; - const tileMapLayerDescriptor = mapModules.createTileMapLayerDescriptor({ - label, - mapType, - colorSchema, - indexPatternId, - geoFieldName, - metricAgg, - metricFieldName, - }); - - if (tileMapLayerDescriptor) { - initialLayers.push(tileMapLayerDescriptor); - } - - return await this.deps.locator.getLocation({ - initialLayers, - filters, - query, - timeRange, - hash, - }); - }; -} - -export interface MapsAppRegionMapLocatorParams extends SerializableRecord { - label: string; - emsLayerId?: string; - leftFieldName?: string; - termsFieldName?: string; - termsSize?: number; - colorSchema: string; - indexPatternId?: string; - metricAgg: string; - metricFieldName?: string; - timeRange?: TimeRange; - filters?: Filter[]; - query?: Query; - hash?: boolean; -} - -export type MapsAppRegionMapLocator = LocatorPublic; - -export const MAPS_APP_REGION_MAP_LOCATOR = 'MAPS_APP_REGION_MAP_LOCATOR' as const; - -export interface MapsAppRegionMapLocatorDependencies { - locator: MapsAppLocator; -} - -export class MapsAppRegionMapLocatorDefinition - implements LocatorDefinition -{ - public readonly id = MAPS_APP_REGION_MAP_LOCATOR; - - constructor(protected readonly deps: MapsAppRegionMapLocatorDependencies) {} - - public readonly getLocation = async (params: MapsAppRegionMapLocatorParams) => { - const { - label, - emsLayerId, - leftFieldName, - termsFieldName, - termsSize, - colorSchema, - indexPatternId, - metricAgg, - metricFieldName, - filters, - query, - timeRange, - hash = true, - } = params; - const mapModules = await lazyLoadMapModules(); - const initialLayers = [] as unknown as LayerDescriptor[] & SerializableRecord; - const regionMapLayerDescriptor = mapModules.createRegionMapLayerDescriptor({ - label, - emsLayerId, - leftFieldName, - termsFieldName, - termsSize, - colorSchema, - indexPatternId, - metricAgg, - metricFieldName, - }); - if (regionMapLayerDescriptor) { - initialLayers.push(regionMapLayerDescriptor); - } - - return await this.deps.locator.getLocation({ - initialLayers, - filters, - query, - timeRange, - hash, - }); - }; -} diff --git a/x-pack/plugins/maps/public/locators/map_locator/get_location.ts b/x-pack/plugins/maps/public/locators/map_locator/get_location.ts new file mode 100644 index 0000000000000..2a451183dd8e0 --- /dev/null +++ b/x-pack/plugins/maps/public/locators/map_locator/get_location.ts @@ -0,0 +1,50 @@ +/* + * 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 rison from '@kbn/rison'; +import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; +import type { GlobalQueryStateFromUrl } from '@kbn/data-plugin/public'; +import type { Filter, Query } from '@kbn/es-query'; +import { isFilterPinned } from '@kbn/es-query'; +import { INITIAL_LAYERS_KEY, APP_ID } from '../../../common/constants'; +import type { MapsAppLocatorDependencies, MapsAppLocatorParams } from './types'; + +export function getLocation(params: MapsAppLocatorParams, deps: MapsAppLocatorDependencies) { + const { mapId, filters, query, refreshInterval, timeRange, initialLayers, hash } = params; + const useHash = hash ?? deps.useHash; + const appState: { + query?: Query; + filters?: Filter[]; + vis?: unknown; + } = {}; + const queryState: GlobalQueryStateFromUrl = {}; + + if (query) appState.query = query; + if (filters && filters.length) appState.filters = filters?.filter((f) => !isFilterPinned(f)); + if (timeRange) queryState.time = timeRange; + if (filters && filters.length) queryState.filters = filters?.filter((f) => isFilterPinned(f)); + if (refreshInterval) queryState.refreshInterval = refreshInterval; + + let path = `/map#/${mapId || ''}`; + path = setStateToKbnUrl('_g', queryState, { useHash }, path); + path = setStateToKbnUrl('_a', appState, { useHash }, path); + + if (initialLayers && initialLayers.length) { + const risonEncodedInitialLayers = rison.encodeArray(initialLayers); + path = `${path}&${INITIAL_LAYERS_KEY}=${encodeURIComponent(risonEncodedInitialLayers)}`; + } + + return { + app: APP_ID, + path, + state: params.dataViewSpec + ? { + dataViewSpec: params.dataViewSpec, + } + : {}, + }; +} diff --git a/x-pack/plugins/maps/public/locators.test.ts b/x-pack/plugins/maps/public/locators/map_locator/locator_definition.test.ts similarity index 92% rename from x-pack/plugins/maps/public/locators.test.ts rename to x-pack/plugins/maps/public/locators/map_locator/locator_definition.test.ts index cc954d5f73717..d281ad00c2b59 100644 --- a/x-pack/plugins/maps/public/locators.test.ts +++ b/x-pack/plugins/maps/public/locators/map_locator/locator_definition.test.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../common/constants'; +import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../../../common/constants'; import { FilterStateStore } from '@kbn/es-query'; -import { MapsAppLocatorDefinition } from './locators'; -import { SerializableRecord } from '@kbn/utility-types'; -import { LayerDescriptor } from '../common/descriptor_types'; +import { MapsAppLocatorDefinition } from './locator_definition'; +import type { SerializableRecord } from '@kbn/utility-types'; +import type { LayerDescriptor } from '../../../common/descriptor_types'; const MAP_ID: string = '2c9c1f60-1909-11e9-919b-ffe5949a18d2'; const LAYER_ID: string = '13823000-99b9-11ea-9eb6-d9e8adceb647'; diff --git a/x-pack/plugins/maps/public/locators/map_locator/locator_definition.ts b/x-pack/plugins/maps/public/locators/map_locator/locator_definition.ts new file mode 100644 index 0000000000000..7c2d71ebeb0dd --- /dev/null +++ b/x-pack/plugins/maps/public/locators/map_locator/locator_definition.ts @@ -0,0 +1,22 @@ +/* + * 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 { LocatorDefinition } from '@kbn/share-plugin/public'; +import type { MapsAppLocatorDependencies, MapsAppLocatorParams } from './types'; + +export const MAPS_APP_LOCATOR = 'MAPS_APP_LOCATOR' as const; + +export class MapsAppLocatorDefinition implements LocatorDefinition { + public readonly id = MAPS_APP_LOCATOR; + + constructor(protected readonly deps: MapsAppLocatorDependencies) {} + + public readonly getLocation = async (params: MapsAppLocatorParams) => { + const { getLocation } = await import('./get_location'); + return getLocation(params, this.deps); + }; +} diff --git a/x-pack/plugins/maps/public/locators/map_locator/types.ts b/x-pack/plugins/maps/public/locators/map_locator/types.ts new file mode 100644 index 0000000000000..bf32465cb5cc3 --- /dev/null +++ b/x-pack/plugins/maps/public/locators/map_locator/types.ts @@ -0,0 +1,64 @@ +/* + * 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 { SerializableRecord } from '@kbn/utility-types'; +import type { Filter, TimeRange, Query } from '@kbn/es-query'; +import type { DataViewSpec } from '@kbn/data-views-plugin/public'; +import type { RefreshInterval } from '@kbn/data-plugin/public'; +import type { LocatorPublic } from '@kbn/share-plugin/public'; +import type { LayerDescriptor } from '../../../common/descriptor_types'; + +export interface MapsAppLocatorParams extends SerializableRecord { + /** + * If given, it will load the given map else will load the create a new map page. + */ + mapId?: string; + + /** + * Optionally set the time range in the time picker. + */ + timeRange?: TimeRange; + + /** + * Optionally set the initial Layers. + */ + initialLayers?: LayerDescriptor[] & SerializableRecord; + + /** + * Optionally set the refresh interval. + */ + refreshInterval?: RefreshInterval & SerializableRecord; + + /** + * Optionally apply filers. NOTE: if given and used in conjunction with `mapId`, and the + * saved map has filters saved with it, this will _replace_ those filters. + */ + filters?: Filter[]; + + /** + * Optionally set a query. NOTE: if given and used in conjunction with `mapId`, and the + * saved map has a query saved with it, this will _replace_ that query. + */ + query?: Query; + + /** + * If not given, will use the uiSettings configuration for `storeInSessionStorage`. useHash determines + * whether to hash the data in the url to avoid url length issues. + */ + hash?: boolean; + + /** + * Optionally pass adhoc data view spec. + */ + dataViewSpec?: DataViewSpec; +} + +export type MapsAppLocator = LocatorPublic; + +export interface MapsAppLocatorDependencies { + useHash: boolean; +} diff --git a/x-pack/plugins/maps/public/locators/region_map_locator/get_location.ts b/x-pack/plugins/maps/public/locators/region_map_locator/get_location.ts new file mode 100644 index 0000000000000..b7706fe8c2e4a --- /dev/null +++ b/x-pack/plugins/maps/public/locators/region_map_locator/get_location.ts @@ -0,0 +1,55 @@ +/* + * 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 { SerializableRecord } from '@kbn/utility-types'; +import type { LayerDescriptor } from '../../../common/descriptor_types'; +import { createRegionMapLayerDescriptor } from '../../classes/layers/create_region_map_layer_descriptor'; +import type { MapsAppRegionMapLocatorParams, MapsAppRegionMapLocatorDependencies } from './types'; + +export async function getLocation( + params: MapsAppRegionMapLocatorParams, + deps: MapsAppRegionMapLocatorDependencies +) { + const { + label, + emsLayerId, + leftFieldName, + termsFieldName, + termsSize, + colorSchema, + indexPatternId, + metricAgg, + metricFieldName, + filters, + query, + timeRange, + hash = true, + } = params; + const initialLayers = [] as unknown as LayerDescriptor[] & SerializableRecord; + const regionMapLayerDescriptor = createRegionMapLayerDescriptor({ + label, + emsLayerId, + leftFieldName, + termsFieldName, + termsSize, + colorSchema, + indexPatternId, + metricAgg, + metricFieldName, + }); + if (regionMapLayerDescriptor) { + initialLayers.push(regionMapLayerDescriptor); + } + + return await deps.locator.getLocation({ + initialLayers, + filters, + query, + timeRange, + hash, + }); +} diff --git a/x-pack/plugins/maps/public/locators/region_map_locator/locator_definition.ts b/x-pack/plugins/maps/public/locators/region_map_locator/locator_definition.ts new file mode 100644 index 0000000000000..b8b7e0fba3cb8 --- /dev/null +++ b/x-pack/plugins/maps/public/locators/region_map_locator/locator_definition.ts @@ -0,0 +1,24 @@ +/* + * 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 { LocatorDefinition } from '@kbn/share-plugin/public'; +import type { MapsAppRegionMapLocatorParams, MapsAppRegionMapLocatorDependencies } from './types'; + +export const MAPS_APP_REGION_MAP_LOCATOR = 'MAPS_APP_REGION_MAP_LOCATOR' as const; + +export class MapsAppRegionMapLocatorDefinition + implements LocatorDefinition +{ + public readonly id = MAPS_APP_REGION_MAP_LOCATOR; + + constructor(protected readonly deps: MapsAppRegionMapLocatorDependencies) {} + + public readonly getLocation = async (params: MapsAppRegionMapLocatorParams) => { + const { getLocation } = await import('./get_location'); + return getLocation(params, this.deps); + }; +} diff --git a/x-pack/plugins/maps/public/locators/region_map_locator/types.ts b/x-pack/plugins/maps/public/locators/region_map_locator/types.ts new file mode 100644 index 0000000000000..f42ee3554ee0c --- /dev/null +++ b/x-pack/plugins/maps/public/locators/region_map_locator/types.ts @@ -0,0 +1,33 @@ +/* + * 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 { SerializableRecord } from '@kbn/utility-types'; +import type { Filter, TimeRange, Query } from '@kbn/es-query'; +import type { LocatorPublic } from '@kbn/share-plugin/public'; +import type { MapsAppLocator } from '../map_locator/types'; + +export interface MapsAppRegionMapLocatorParams extends SerializableRecord { + label: string; + emsLayerId?: string; + leftFieldName?: string; + termsFieldName?: string; + termsSize?: number; + colorSchema: string; + indexPatternId?: string; + metricAgg: string; + metricFieldName?: string; + timeRange?: TimeRange; + filters?: Filter[]; + query?: Query; + hash?: boolean; +} + +export type MapsAppRegionMapLocator = LocatorPublic; + +export interface MapsAppRegionMapLocatorDependencies { + locator: MapsAppLocator; +} diff --git a/x-pack/plugins/maps/public/locators/tile_map_locator/get_location.ts b/x-pack/plugins/maps/public/locators/tile_map_locator/get_location.ts new file mode 100644 index 0000000000000..fb7bb0b7d3bd3 --- /dev/null +++ b/x-pack/plugins/maps/public/locators/tile_map_locator/get_location.ts @@ -0,0 +1,52 @@ +/* + * 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 { SerializableRecord } from '@kbn/utility-types'; +import type { LayerDescriptor } from '../../../common/descriptor_types'; +import { createTileMapLayerDescriptor } from '../../classes/layers/create_tile_map_layer_descriptor'; +import type { MapsAppTileMapLocatorParams, MapsAppTileMapLocatorDependencies } from './types'; + +export async function getLocation( + params: MapsAppTileMapLocatorParams, + deps: MapsAppTileMapLocatorDependencies +) { + const { + label, + mapType, + colorSchema, + indexPatternId, + geoFieldName, + metricAgg, + metricFieldName, + filters, + query, + timeRange, + hash = true, + } = params; + const initialLayers = [] as unknown as LayerDescriptor[] & SerializableRecord; + const tileMapLayerDescriptor = createTileMapLayerDescriptor({ + label, + mapType, + colorSchema, + indexPatternId, + geoFieldName, + metricAgg, + metricFieldName, + }); + + if (tileMapLayerDescriptor) { + initialLayers.push(tileMapLayerDescriptor); + } + + return await deps.locator.getLocation({ + initialLayers, + filters, + query, + timeRange, + hash, + }); +} diff --git a/x-pack/plugins/maps/public/locators/tile_map_locator/locator_definition.ts b/x-pack/plugins/maps/public/locators/tile_map_locator/locator_definition.ts new file mode 100644 index 0000000000000..e511d641fc5ae --- /dev/null +++ b/x-pack/plugins/maps/public/locators/tile_map_locator/locator_definition.ts @@ -0,0 +1,24 @@ +/* + * 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 { LocatorDefinition } from '@kbn/share-plugin/public'; +import type { MapsAppTileMapLocatorParams, MapsAppTileMapLocatorDependencies } from './types'; + +export const MAPS_APP_TILE_MAP_LOCATOR = 'MAPS_APP_TILE_MAP_LOCATOR' as const; + +export class MapsAppTileMapLocatorDefinition + implements LocatorDefinition +{ + public readonly id = MAPS_APP_TILE_MAP_LOCATOR; + + constructor(protected readonly deps: MapsAppTileMapLocatorDependencies) {} + + public readonly getLocation = async (params: MapsAppTileMapLocatorParams) => { + const { getLocation } = await import('./get_location'); + return getLocation(params, this.deps); + }; +} diff --git a/x-pack/plugins/maps/public/locators/tile_map_locator/types.ts b/x-pack/plugins/maps/public/locators/tile_map_locator/types.ts new file mode 100644 index 0000000000000..5743feca993d6 --- /dev/null +++ b/x-pack/plugins/maps/public/locators/tile_map_locator/types.ts @@ -0,0 +1,31 @@ +/* + * 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 { SerializableRecord } from '@kbn/utility-types'; +import type { Filter, TimeRange, Query } from '@kbn/es-query'; +import type { LocatorPublic } from '@kbn/share-plugin/public'; +import type { MapsAppLocator } from '../map_locator/types'; + +export interface MapsAppTileMapLocatorParams extends SerializableRecord { + label: string; + mapType: string; + colorSchema: string; + indexPatternId?: string; + geoFieldName?: string; + metricAgg: string; + metricFieldName?: string; + timeRange?: TimeRange; + filters?: Filter[]; + query?: Query; + hash?: boolean; +} + +export type MapsAppTileMapLocator = LocatorPublic; + +export interface MapsAppTileMapLocatorDependencies { + locator: MapsAppLocator; +} diff --git a/x-pack/plugins/maps/public/map_attribute_service.ts b/x-pack/plugins/maps/public/map_attribute_service.ts index b93c5421f5cb3..5a4d3e8cf0038 100644 --- a/x-pack/plugins/maps/public/map_attribute_service.ts +++ b/x-pack/plugins/maps/public/map_attribute_service.ts @@ -10,8 +10,7 @@ import type { ResolvedSimpleSavedObject } from '@kbn/core/public'; import { AttributeService } from '@kbn/embeddable-plugin/public'; import type { OnSaveProps } from '@kbn/saved-objects-plugin/public'; import type { MapAttributes } from '../common/content_management'; -import { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; -import { getMapEmbeddableDisplayName } from '../common/i18n_getters'; +import { MAP_EMBEDDABLE_NAME, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; import { getCoreOverlays, getEmbeddableService } from './kibana_services'; import { extractReferences, injectReferences } from '../common/migrations/references'; import { mapsClient, checkForDuplicateTitle } from './content_management'; @@ -108,7 +107,7 @@ export function getMapAttributeService(): MapAttributeService { copyOnSave: false, lastSavedTitle: '', isTitleDuplicateConfirmed: props.isTitleDuplicateConfirmed, - getDisplayName: getMapEmbeddableDisplayName, + getDisplayName: () => MAP_EMBEDDABLE_NAME, onTitleDuplicate: props.onTitleDuplicate, }, { diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 0d558699e6e51..75c4211c54d58 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -56,32 +56,29 @@ import { tileMapRenderer, tileMapVisType, } from './legacy_visualizations'; -import { - MapsAppLocatorDefinition, - MapsAppRegionMapLocatorDefinition, - MapsAppTileMapLocatorDefinition, -} from './locators'; +import { MapsAppLocatorDefinition } from './locators/map_locator/locator_definition'; +import { MapsAppTileMapLocatorDefinition } from './locators/tile_map_locator/locator_definition'; +import { MapsAppRegionMapLocatorDefinition } from './locators/region_map_locator/locator_definition'; import { registerLicensedFeatures, setLicensingPluginStart } from './licensed_features'; import { registerSource } from './classes/sources/source_registry'; -import { registerLayerWizardExternal } from './classes/layers'; +import { registerLayerWizardExternal } from './classes/layers/wizards/layer_wizard_registry'; import { createLayerDescriptors, MapsSetupApi, MapsStartApi, suggestEMSTermJoinConfig, } from './api'; -import { lazyLoadMapModules } from './lazy_load_bundle'; -import { getAppTitle } from '../common/i18n_getters'; import { MapsXPackConfig, MapsConfigType } from '../config'; import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; -import { filterByMapExtentAction } from './trigger_actions/filter_by_map_extent_action'; -import { synchronizeMovementAction } from './trigger_actions/synchronize_movement_action'; +import { filterByMapExtentAction } from './trigger_actions/filter_by_map_extent/action'; +import { synchronizeMovementAction } from './trigger_actions/synchronize_movement/action'; import { visualizeGeoFieldAction } from './trigger_actions/visualize_geo_field_action'; -import { APP_ICON_SOLUTION, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; +import { APP_NAME, APP_ICON_SOLUTION, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; import { getMapsVisTypeAlias } from './maps_vis_type_alias'; import { featureCatalogueEntry } from './feature_catalogue_entry'; import { setIsCloudEnabled, setMapAppConfig, setStartServices } from './kibana_services'; -import { MapInspectorView, VectorTileInspectorView } from './inspector'; +import { MapInspectorView } from './inspector/map_adapter/map_inspector_view'; +import { VectorTileInspectorView } from './inspector/vector_tile_adapter/vector_tile_inspector_view'; import { setupLensChoroplethChart } from './lens'; import { CONTENT_ID, LATEST_VERSION } from '../common/content_management'; @@ -193,7 +190,7 @@ export class MapsPlugin core.application.register({ id: APP_ID, - title: getAppTitle(), + title: APP_NAME, order: 4000, icon: `plugins/${APP_ID}/icon.svg`, euiIconType: APP_ICON_SOLUTION, @@ -202,7 +199,7 @@ export class MapsPlugin const [coreStart, { savedObjectsTagging }] = await core.getStartServices(); const UsageTracker = plugins.usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment; - const { renderApp } = await lazyLoadMapModules(); + const { renderApp } = await import('./render_app'); return renderApp(params, { coreStart, AppUsageTracker: UsageTracker, savedObjectsTagging }); }, }); @@ -212,7 +209,7 @@ export class MapsPlugin version: { latest: LATEST_VERSION, }, - name: getAppTitle(), + name: APP_NAME, }); setupLensChoroplethChart(core, plugins.expressions, plugins.lens); diff --git a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx index e1ce3e801aac3..95063f728a8fc 100644 --- a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx +++ b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx @@ -13,7 +13,7 @@ import { TableListView } from '@kbn/content-management-table-list'; import type { UserContentCommonSchema } from '@kbn/content-management-table-list'; import type { MapItem } from '../../../common/content_management'; -import { APP_ID, getEditPath, MAP_PATH } from '../../../common/constants'; +import { APP_ID, APP_NAME, getEditPath, MAP_PATH } from '../../../common/constants'; import { getMapsCapabilities, getCoreChrome, @@ -22,7 +22,6 @@ import { getUiSettings, getUsageCollection, } from '../../kibana_services'; -import { getAppTitle } from '../../../common/i18n_getters'; import { mapsClient } from '../../content_management'; const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; @@ -73,8 +72,8 @@ function MapsListViewComp({ history }: Props) { const listingLimit = getUiSettings().get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = getUiSettings().get(SAVED_OBJECTS_PER_PAGE_SETTING); - getCoreChrome().docTitle.change(getAppTitle()); - getCoreChrome().setBreadcrumbs([{ text: getAppTitle() }]); + getCoreChrome().docTitle.change(APP_NAME); + getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]); const findMaps = useCallback( async ( @@ -128,7 +127,7 @@ function MapsListViewComp({ history }: Props) { entityNamePlural={i18n.translate('xpack.maps.mapListing.entityNamePlural', { defaultMessage: 'maps', })} - tableListTitle={getAppTitle()} + tableListTitle={APP_NAME} onClickTitle={({ id }) => history.push(getEditPath(id))} /> ); diff --git a/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx b/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx index 4220e25212f2a..ceb9a487fffdc 100644 --- a/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx +++ b/x-pack/plugins/maps/public/routes/map_page/map_app/map_app.tsx @@ -47,8 +47,12 @@ import { AppStateManager, startAppStateSyncing } from '../url_state'; import { MapContainer } from '../../../connected_components/map_container'; import { getIndexPatternsFromIds } from '../../../index_pattern_util'; import { getTopNavConfig } from '../top_nav_config'; -import { getEditPath, getFullPath, APP_ID } from '../../../../common/constants'; -import { getMapEmbeddableDisplayName } from '../../../../common/i18n_getters'; +import { + getEditPath, + getFullPath, + APP_ID, + MAP_EMBEDDABLE_NAME, +} from '../../../../common/constants'; import { getInitialQuery, getInitialRefreshConfig, @@ -432,7 +436,7 @@ export class MapApp extends React.Component { await spaces.ui.redirectLegacyUrl({ path: newPath, aliasPurpose: sharingSavedObjectProps.aliasPurpose, - objectNoun: getMapEmbeddableDisplayName(), + objectNoun: MAP_EMBEDDABLE_NAME, }); return; } @@ -547,7 +551,7 @@ export class MapApp extends React.Component { const spaces = getSpacesApi(); return spaces && sharingSavedObjectProps?.outcome === 'conflict' ? spaces.ui.components.getLegacyUrlConflict({ - objectNoun: getMapEmbeddableDisplayName(), + objectNoun: MAP_EMBEDDABLE_NAME, currentObjectId: this.props.savedMap.getSavedObjectId()!, otherObjectId: sharingSavedObjectProps.aliasTargetId!, otherObjectPath: `${getEditPath(sharingSavedObjectProps.aliasTargetId!)}${ diff --git a/x-pack/plugins/maps/public/routes/map_page/saved_map/get_breadcrumbs.tsx b/x-pack/plugins/maps/public/routes/map_page/saved_map/get_breadcrumbs.tsx index ca3022043cd9f..344722a480b08 100644 --- a/x-pack/plugins/maps/public/routes/map_page/saved_map/get_breadcrumbs.tsx +++ b/x-pack/plugins/maps/public/routes/map_page/saved_map/get_breadcrumbs.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { ScopedHistory } from '@kbn/core/public'; import { getCoreOverlays, getNavigateToApp } from '../../../kibana_services'; -import { getAppTitle } from '../../../../common/i18n_getters'; +import { APP_NAME } from '../../../../common/constants'; export const unsavedChangesWarning = i18n.translate( 'xpack.maps.breadCrumbs.unsavedChangesWarning', @@ -49,7 +49,7 @@ export function getBreadcrumbs({ if (!isByValue) { breadcrumbs.push({ - text: getAppTitle(), + text: APP_NAME, onClick: async () => { if (getHasUnsavedChanges()) { const confirmed = await getCoreOverlays().openConfirm(unsavedChangesWarning, { diff --git a/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx b/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx index 8b7efa9335858..6bef5987dd9f8 100644 --- a/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx +++ b/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx @@ -27,8 +27,8 @@ import { getSavedObjectsTagging, getPresentationUtilContext, } from '../../kibana_services'; +import { MAP_EMBEDDABLE_NAME } from '../../../common/constants'; import { SavedMap } from './saved_map'; -import { getMapEmbeddableDisplayName } from '../../../common/i18n_getters'; import { checkForDuplicateTitle } from '../../content_management'; const SavedObjectSaveModalDashboard = withSuspense(LazySavedObjectSaveModalDashboard); @@ -179,7 +179,7 @@ export function getTopNavConfig({ copyOnSave: props.newCopyOnSave, lastSavedTitle: savedMap.getSavedObjectId() ? savedMap.getTitle() : '', isTitleDuplicateConfirmed: props.isTitleDuplicateConfirmed, - getDisplayName: getMapEmbeddableDisplayName, + getDisplayName: () => MAP_EMBEDDABLE_NAME, onTitleDuplicate: props.onTitleDuplicate, }, { diff --git a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/action.ts similarity index 63% rename from x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx rename to x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/action.ts index c2183774cf677..5e21c28d69fe1 100644 --- a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_action.tsx +++ b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/action.ts @@ -5,25 +5,13 @@ * 2.0. */ -import React from 'react'; import { i18n } from '@kbn/i18n'; -import { Embeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public'; -import { createReactOverlays } from '@kbn/kibana-react-plugin/public'; +import type { Embeddable } from '@kbn/embeddable-plugin/public'; import { createAction } from '@kbn/ui-actions-plugin/public'; -import { isLegacyMap } from '../legacy_visualizations'; -import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; -import { getCore } from '../kibana_services'; +import type { FilterByMapExtentActionContext, FilterByMapExtentInput } from './types'; export const FILTER_BY_MAP_EXTENT = 'FILTER_BY_MAP_EXTENT'; -interface FilterByMapExtentInput extends EmbeddableInput { - filterByMapExtent: boolean; -} - -interface FilterByMapExtentActionContext { - embeddable: Embeddable; -} - function getContainerLabel(embeddable: Embeddable) { return embeddable.parent?.type === 'dashboard' ? i18n.translate('xpack.maps.filterByMapExtentMenuItem.dashboardLabel', { @@ -58,20 +46,12 @@ export const filterByMapExtentAction = createAction { return 'filter'; }, - isCompatible: async ({ embeddable }: FilterByMapExtentActionContext) => { - return ( - (embeddable.type === MAP_SAVED_OBJECT_TYPE || isLegacyMap(embeddable)) && - !embeddable.getInput().disableTriggers - ); + isCompatible: async (context: FilterByMapExtentActionContext) => { + const { isCompatible } = await import('./is_compatible'); + return isCompatible(context); }, execute: async (context: FilterByMapExtentActionContext) => { - const { FilterByMapExtentModal } = await import('./filter_by_map_extent_modal'); - const { openModal } = createReactOverlays(getCore()); - const modalSession = openModal( - modalSession.close()} - title={getDisplayName(context.embeddable)} - /> - ); + const { openModal } = await import('./modal'); + openModal(getDisplayName(context.embeddable)); }, }); diff --git a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/is_compatible.ts b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/is_compatible.ts new file mode 100644 index 0000000000000..32892b6fdc18b --- /dev/null +++ b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/is_compatible.ts @@ -0,0 +1,17 @@ +/* + * 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 { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants'; +import { isLegacyMap } from '../../legacy_visualizations/is_legacy_map'; +import type { FilterByMapExtentActionContext } from './types'; + +export function isCompatible({ embeddable }: FilterByMapExtentActionContext) { + return ( + (embeddable.type === MAP_SAVED_OBJECT_TYPE || isLegacyMap(embeddable)) && + !embeddable.getInput().disableTriggers + ); +} diff --git a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_modal.tsx b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/modal.tsx similarity index 78% rename from x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_modal.tsx rename to x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/modal.tsx index 206f754ae65bb..9e5127f9329ba 100644 --- a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent_modal.tsx +++ b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/modal.tsx @@ -14,14 +14,23 @@ import { EuiSwitch, EuiSwitchEvent, } from '@elastic/eui'; -import { mapEmbeddablesSingleton } from '../embeddable/map_embeddables_singleton'; +import { createReactOverlays } from '@kbn/kibana-react-plugin/public'; +import { mapEmbeddablesSingleton } from '../../embeddable/map_embeddables_singleton'; +import { getCore } from '../../kibana_services'; + +export function openModal(title: string) { + const { openModal: reactOverlaysOpenModal } = createReactOverlays(getCore()); + const modalSession = reactOverlaysOpenModal( + modalSession.close()} title={title} /> + ); +} interface Props { onClose: () => void; title: string; } -export class FilterByMapExtentModal extends Component { +class FilterByMapExtentModal extends Component { _renderSwitches() { return mapEmbeddablesSingleton.getMapPanels().map((mapPanel) => { return ( diff --git a/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/types.ts b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/types.ts new file mode 100644 index 0000000000000..5587588b60730 --- /dev/null +++ b/x-pack/plugins/maps/public/trigger_actions/filter_by_map_extent/types.ts @@ -0,0 +1,16 @@ +/* + * 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 { Embeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public'; + +export interface FilterByMapExtentInput extends EmbeddableInput { + filterByMapExtent: boolean; +} + +export interface FilterByMapExtentActionContext { + embeddable: Embeddable; +} diff --git a/x-pack/plugins/maps/public/trigger_actions/get_maps_link.ts b/x-pack/plugins/maps/public/trigger_actions/get_maps_link.ts new file mode 100644 index 0000000000000..da0e8bac59235 --- /dev/null +++ b/x-pack/plugins/maps/public/trigger_actions/get_maps_link.ts @@ -0,0 +1,50 @@ +/* + * 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 { v4 as uuidv4 } from 'uuid'; +import type { Query } from '@kbn/es-query'; +import type { SerializableRecord } from '@kbn/utility-types'; +import type { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; +import { getIndexPatternService, getData, getShareService } from '../kibana_services'; +import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../../common/constants'; +import type { LayerDescriptor } from '../../common/descriptor_types'; +import type { MapsAppLocator } from '../locators/map_locator/types'; +import { MAPS_APP_LOCATOR } from '../locators/map_locator/locator_definition'; + +export const getMapsLink = async (context: VisualizeFieldContext) => { + const dataView = await getIndexPatternService().get(context.dataViewSpec.id!); + // create initial layer descriptor + const hasTooltips = + context?.contextualFields?.length && context?.contextualFields[0] !== '_source'; + const initialLayers = [ + { + id: uuidv4(), + visible: true, + type: LAYER_TYPE.MVT_VECTOR, + sourceDescriptor: { + id: uuidv4(), + type: SOURCE_TYPES.ES_SEARCH, + tooltipProperties: hasTooltips ? context.contextualFields : [], + label: dataView.getIndexPattern(), + indexPatternId: context.dataViewSpec.id, + geoField: context.fieldName, + scalingType: SCALING_TYPES.MVT, + }, + }, + ]; + + const locator = getShareService().url.locators.get(MAPS_APP_LOCATOR) as MapsAppLocator; + const location = await locator.getLocation({ + filters: getData().query.filterManager.getFilters(), + query: getData().query.queryString.getQuery() as Query, + initialLayers: initialLayers as unknown as LayerDescriptor[] & SerializableRecord, + timeRange: getData().query.timefilter.timefilter.getTime(), + dataViewSpec: context.dataViewSpec, + }); + + return location; +}; diff --git a/x-pack/plugins/maps/public/trigger_actions/synchronize_movement/action.ts b/x-pack/plugins/maps/public/trigger_actions/synchronize_movement/action.ts new file mode 100644 index 0000000000000..3a3fd78072865 --- /dev/null +++ b/x-pack/plugins/maps/public/trigger_actions/synchronize_movement/action.ts @@ -0,0 +1,40 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { createAction } from '@kbn/ui-actions-plugin/public'; +import type { SynchronizeMovementActionContext } from './types'; + +export const SYNCHRONIZE_MOVEMENT_ACTION = 'SYNCHRONIZE_MOVEMENT_ACTION'; + +export const synchronizeMovementAction = createAction({ + id: SYNCHRONIZE_MOVEMENT_ACTION, + type: SYNCHRONIZE_MOVEMENT_ACTION, + order: 21, + getDisplayName: ({ embeddable }: SynchronizeMovementActionContext) => { + return i18n.translate('xpack.maps.synchronizeMovementAction.title', { + defaultMessage: 'Synchronize map movement', + }); + }, + getDisplayNameTooltip: () => { + return i18n.translate('xpack.maps.synchronizeMovementAction.tooltipContent', { + defaultMessage: + 'Synchronize maps, so that if you zoom and pan in one map, the movement is reflected in other maps', + }); + }, + getIconType: () => { + return 'crosshairs'; + }, + isCompatible: async (context: SynchronizeMovementActionContext) => { + const { isCompatible } = await import('./is_compatible'); + return isCompatible(context); + }, + execute: async (context: SynchronizeMovementActionContext) => { + const { openModal } = await import('./modal'); + openModal(); + }, +}); diff --git a/x-pack/plugins/maps/public/trigger_actions/synchronize_movement/is_compatible.ts b/x-pack/plugins/maps/public/trigger_actions/synchronize_movement/is_compatible.ts new file mode 100644 index 0000000000000..ef73f8bb23d11 --- /dev/null +++ b/x-pack/plugins/maps/public/trigger_actions/synchronize_movement/is_compatible.ts @@ -0,0 +1,32 @@ +/* + * 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 { Embeddable as LensEmbeddable } from '@kbn/lens-plugin/public'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants'; +import { isLegacyMap } from '../../legacy_visualizations/is_legacy_map'; +import { mapEmbeddablesSingleton } from '../../embeddable/map_embeddables_singleton'; +import type { SynchronizeMovementActionContext } from './types'; + +export function isCompatible({ embeddable }: SynchronizeMovementActionContext) { + if (!mapEmbeddablesSingleton.hasMultipleMaps()) { + return false; + } + + if ( + embeddable.type === 'lens' && + typeof (embeddable as LensEmbeddable).getSavedVis === 'function' && + (embeddable as LensEmbeddable).getSavedVis()?.visualizationType === 'lnsChoropleth' + ) { + return true; + } + + if (isLegacyMap(embeddable)) { + return true; + } + + return embeddable.type === MAP_SAVED_OBJECT_TYPE; +} diff --git a/x-pack/plugins/maps/public/trigger_actions/synchronize_movement_modal.tsx b/x-pack/plugins/maps/public/trigger_actions/synchronize_movement/modal.tsx similarity index 84% rename from x-pack/plugins/maps/public/trigger_actions/synchronize_movement_modal.tsx rename to x-pack/plugins/maps/public/trigger_actions/synchronize_movement/modal.tsx index c6a1dae7eb36b..fa3ad5bfa8031 100644 --- a/x-pack/plugins/maps/public/trigger_actions/synchronize_movement_modal.tsx +++ b/x-pack/plugins/maps/public/trigger_actions/synchronize_movement/modal.tsx @@ -15,13 +15,22 @@ import { EuiSwitch, EuiSwitchEvent, } from '@elastic/eui'; -import { mapEmbeddablesSingleton } from '../embeddable/map_embeddables_singleton'; +import { createReactOverlays } from '@kbn/kibana-react-plugin/public'; +import { mapEmbeddablesSingleton } from '../../embeddable/map_embeddables_singleton'; +import { getCore } from '../../kibana_services'; + +export function openModal() { + const { openModal: reactOverlaysOpenModal } = createReactOverlays(getCore()); + const modalSession = reactOverlaysOpenModal( + modalSession.close()} /> + ); +} interface Props { onClose: () => void; } -export class SynchronizeMovementModal extends Component { +class SynchronizeMovementModal extends Component { _renderSwitches() { const mapPanels = mapEmbeddablesSingleton.getMapPanels(); diff --git a/x-pack/plugins/task_manager/server/lib/calculate_health_status.mock.ts b/x-pack/plugins/maps/public/trigger_actions/synchronize_movement/types.ts similarity index 58% rename from x-pack/plugins/task_manager/server/lib/calculate_health_status.mock.ts rename to x-pack/plugins/maps/public/trigger_actions/synchronize_movement/types.ts index f34a26560133b..8b0060ab1efe6 100644 --- a/x-pack/plugins/task_manager/server/lib/calculate_health_status.mock.ts +++ b/x-pack/plugins/maps/public/trigger_actions/synchronize_movement/types.ts @@ -5,10 +5,8 @@ * 2.0. */ -const createCalculateHealthStatusMock = () => { - return jest.fn(); -}; +import type { Embeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public'; -export const calculateHealthStatusMock = { - create: createCalculateHealthStatusMock, -}; +export interface SynchronizeMovementActionContext { + embeddable: Embeddable; +} diff --git a/x-pack/plugins/maps/public/trigger_actions/synchronize_movement_action.tsx b/x-pack/plugins/maps/public/trigger_actions/synchronize_movement_action.tsx deleted file mode 100644 index 7116e55fd521d..0000000000000 --- a/x-pack/plugins/maps/public/trigger_actions/synchronize_movement_action.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 React from 'react'; -import { i18n } from '@kbn/i18n'; -import { createReactOverlays } from '@kbn/kibana-react-plugin/public'; -import { Embeddable, EmbeddableInput } from '@kbn/embeddable-plugin/public'; -import { createAction } from '@kbn/ui-actions-plugin/public'; -import type { Embeddable as LensEmbeddable } from '@kbn/lens-plugin/public'; -import { isLegacyMap } from '../legacy_visualizations'; -import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; -import { getCore } from '../kibana_services'; - -export const SYNCHRONIZE_MOVEMENT_ACTION = 'SYNCHRONIZE_MOVEMENT_ACTION'; - -interface SynchronizeMovementActionContext { - embeddable: Embeddable; -} - -export const synchronizeMovementAction = createAction({ - id: SYNCHRONIZE_MOVEMENT_ACTION, - type: SYNCHRONIZE_MOVEMENT_ACTION, - order: 21, - getDisplayName: ({ embeddable }: SynchronizeMovementActionContext) => { - return i18n.translate('xpack.maps.synchronizeMovementAction.title', { - defaultMessage: 'Synchronize map movement', - }); - }, - getDisplayNameTooltip: () => { - return i18n.translate('xpack.maps.synchronizeMovementAction.tooltipContent', { - defaultMessage: - 'Synchronize maps, so that if you zoom and pan in one map, the movement is reflected in other maps', - }); - }, - getIconType: () => { - return 'crosshairs'; - }, - isCompatible: async ({ embeddable }: SynchronizeMovementActionContext) => { - const { mapEmbeddablesSingleton } = await import('../embeddable/map_embeddables_singleton'); - if (!mapEmbeddablesSingleton.hasMultipleMaps()) { - return false; - } - - if ( - embeddable.type === 'lens' && - typeof (embeddable as LensEmbeddable).getSavedVis === 'function' && - (embeddable as LensEmbeddable).getSavedVis()?.visualizationType === 'lnsChoropleth' - ) { - return true; - } - - if (isLegacyMap(embeddable)) { - return true; - } - - return embeddable.type === MAP_SAVED_OBJECT_TYPE; - }, - execute: async ({ embeddable }: SynchronizeMovementActionContext) => { - const { SynchronizeMovementModal } = await import('./synchronize_movement_modal'); - const { openModal } = createReactOverlays(getCore()); - const modalSession = openModal( - modalSession.close()} /> - ); - }, -}); diff --git a/x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts b/x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts index c1194fec094a3..417ff0d0bf1fe 100644 --- a/x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts +++ b/x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts @@ -5,10 +5,7 @@ * 2.0. */ -import { v4 as uuidv4 } from 'uuid'; import { i18n } from '@kbn/i18n'; -import type { Query } from '@kbn/es-query'; -import type { SerializableRecord } from '@kbn/utility-types'; import { METRIC_TYPE } from '@kbn/analytics'; import { createAction, @@ -18,16 +15,7 @@ import { import { getUsageCollection } from '../kibana_services'; import { APP_ID } from '../../common/constants'; -import { - getVisualizeCapabilities, - getIndexPatternService, - getData, - getShareService, - getCore, -} from '../kibana_services'; -import { MapsAppLocator, MAPS_APP_LOCATOR } from '../locators'; -import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../../common/constants'; -import { LayerDescriptor } from '../../common/descriptor_types'; +import { getVisualizeCapabilities, getCore } from '../kibana_services'; export const visualizeGeoFieldAction = createAction({ id: ACTION_VISUALIZE_GEO_FIELD, @@ -38,6 +26,7 @@ export const visualizeGeoFieldAction = createAction({ }), isCompatible: async () => !!getVisualizeCapabilities().show, getHref: async (context) => { + const { getMapsLink } = await import('./get_maps_link'); const { app, path } = await getMapsLink(context); return getCore().application.getUrlForApp(app, { @@ -46,6 +35,7 @@ export const visualizeGeoFieldAction = createAction({ }); }, execute: async (context) => { + const { getMapsLink } = await import('./get_maps_link'); const { app, path, state } = await getMapsLink(context); const usageCollection = getUsageCollection(); @@ -61,37 +51,3 @@ export const visualizeGeoFieldAction = createAction({ }); }, }); - -const getMapsLink = async (context: VisualizeFieldContext) => { - const dataView = await getIndexPatternService().get(context.dataViewSpec.id!); - // create initial layer descriptor - const hasTooltips = - context?.contextualFields?.length && context?.contextualFields[0] !== '_source'; - const initialLayers = [ - { - id: uuidv4(), - visible: true, - type: LAYER_TYPE.MVT_VECTOR, - sourceDescriptor: { - id: uuidv4(), - type: SOURCE_TYPES.ES_SEARCH, - tooltipProperties: hasTooltips ? context.contextualFields : [], - label: dataView.getIndexPattern(), - indexPatternId: context.dataViewSpec.id, - geoField: context.fieldName, - scalingType: SCALING_TYPES.MVT, - }, - }, - ]; - - const locator = getShareService().url.locators.get(MAPS_APP_LOCATOR) as MapsAppLocator; - const location = await locator.getLocation({ - filters: getData().query.filterManager.getFilters(), - query: getData().query.queryString.getQuery() as Query, - initialLayers: initialLayers as unknown as LayerDescriptor[] & SerializableRecord, - timeRange: getData().query.timefilter.timefilter.getTime(), - dataViewSpec: context.dataViewSpec, - }); - - return location; -}; diff --git a/x-pack/plugins/maps/public/util.ts b/x-pack/plugins/maps/public/util.ts index 364f72c0b564e..bed83da7e8335 100644 --- a/x-pack/plugins/maps/public/util.ts +++ b/x-pack/plugins/maps/public/util.ts @@ -6,11 +6,12 @@ */ import { EMSClient, FileLayer, TMSService } from '@elastic/ems-client'; -import { getTilemap, getEMSSettings, getMapsEmsStart } from './kibana_services'; +import { getEMSSettings, getMapsEmsStart } from './kibana_services'; import { getLicenseId } from './licensed_features'; export function getKibanaTileMap(): unknown { - return getTilemap(); + const mapsEms = getMapsEmsStart(); + return mapsEms.config.tilemap ? mapsEms.config.tilemap : {}; } export async function getEmsFileLayers(): Promise { diff --git a/x-pack/plugins/task_manager/server/lib/calculate_health_status.ts b/x-pack/plugins/task_manager/server/lib/calculate_health_status.ts index 8bd7f5de80810..d900e66360820 100644 --- a/x-pack/plugins/task_manager/server/lib/calculate_health_status.ts +++ b/x-pack/plugins/task_manager/server/lib/calculate_health_status.ts @@ -16,7 +16,7 @@ export function calculateHealthStatus( config: TaskManagerConfig, shouldRunTasks: boolean, logger: Logger -): HealthStatus { +): { status: HealthStatus; reason?: string } { const now = Date.now(); // if "hot" health stats are any more stale than monitored_stats_required_freshness @@ -28,27 +28,35 @@ export function calculateHealthStatus( const requiredColdStatsFreshness: number = config.monitored_aggregated_stats_refresh_rate * 1.5; if (hasStatus(summarizedStats.stats, HealthStatus.Error)) { - return HealthStatus.Error; + return { + status: HealthStatus.Error, + reason: summarizedStats.stats.capacity_estimation?.reason, + }; } // Hot timestamps look at runtime stats which are not available when tasks are not running if (shouldRunTasks) { if (hasExpiredHotTimestamps(summarizedStats, now, requiredHotStatsFreshness)) { - logger.debug('setting HealthStatus.Error because of expired hot timestamps'); - return HealthStatus.Error; + const reason = 'setting HealthStatus.Error because of expired hot timestamps'; + logger.warn(reason); + return { status: HealthStatus.Error, reason }; } } if (hasExpiredColdTimestamps(summarizedStats, now, requiredColdStatsFreshness)) { - logger.debug('setting HealthStatus.Error because of expired cold timestamps'); - return HealthStatus.Error; + const reason = 'setting HealthStatus.Error because of expired cold timestamps'; + logger.warn(reason); + return { status: HealthStatus.Error, reason }; } if (hasStatus(summarizedStats.stats, HealthStatus.Warning)) { - return HealthStatus.Warning; + return { + status: HealthStatus.Warning, + reason: summarizedStats.stats.capacity_estimation?.reason, + }; } - return HealthStatus.OK; + return { status: HealthStatus.OK }; } function hasStatus(stats: RawMonitoringStats['stats'], status: HealthStatus): boolean { diff --git a/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts b/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts index 152f0ae82543e..552648e407fa6 100644 --- a/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts +++ b/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts @@ -41,16 +41,30 @@ describe('logHealthMetrics', () => { const { calculateHealthStatus } = jest.requireMock('./calculate_health_status'); // We must change from OK to Warning - (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.OK); + ( + calculateHealthStatus as jest.Mock<{ status: HealthStatus; reason?: string }> + ).mockImplementation(() => ({ + status: HealthStatus.OK, + })); logHealthMetrics(health, logger, config, true, docLinks); - (calculateHealthStatus as jest.Mock).mockImplementation( - () => HealthStatus.Warning - ); + ( + calculateHealthStatus as jest.Mock<{ status: HealthStatus; reason?: string }> + ).mockImplementation(() => ({ + status: HealthStatus.Warning, + })); logHealthMetrics(health, logger, config, true, docLinks); // We must change from OK to Error - (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.OK); + ( + calculateHealthStatus as jest.Mock<{ status: HealthStatus; reason?: string }> + ).mockImplementation(() => ({ + status: HealthStatus.OK, + })); logHealthMetrics(health, logger, config, true, docLinks); - (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.Error); + ( + calculateHealthStatus as jest.Mock<{ status: HealthStatus; reason?: string }> + ).mockImplementation(() => ({ + status: HealthStatus.Error, + })); logHealthMetrics(health, logger, config, true, docLinks); const debugCalls = (logger as jest.Mocked).debug.mock.calls; @@ -175,9 +189,11 @@ describe('logHealthMetrics', () => { }); const health = getMockMonitoredHealth(); const { calculateHealthStatus } = jest.requireMock('./calculate_health_status'); - (calculateHealthStatus as jest.Mock).mockImplementation( - () => HealthStatus.Warning - ); + ( + calculateHealthStatus as jest.Mock<{ status: HealthStatus; reason?: string }> + ).mockImplementation(() => ({ + status: HealthStatus.Warning, + })); logHealthMetrics(health, logger, config, true, docLinks); @@ -201,7 +217,11 @@ describe('logHealthMetrics', () => { }); const health = getMockMonitoredHealth(); const { calculateHealthStatus } = jest.requireMock('./calculate_health_status'); - (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.Error); + ( + calculateHealthStatus as jest.Mock<{ status: HealthStatus; reason?: string }> + ).mockImplementation(() => ({ + status: HealthStatus.Error, + })); logHealthMetrics(health, logger, config, true, docLinks); diff --git a/x-pack/plugins/task_manager/server/lib/log_health_metrics.ts b/x-pack/plugins/task_manager/server/lib/log_health_metrics.ts index eff71b28f8abb..bddafb802b41b 100644 --- a/x-pack/plugins/task_manager/server/lib/log_health_metrics.ts +++ b/x-pack/plugins/task_manager/server/lib/log_health_metrics.ts @@ -40,12 +40,9 @@ export function logHealthMetrics( capacity_estimation: undefined, }, }; - const statusWithoutCapacity = calculateHealthStatus( - healthWithoutCapacity, - config, - shouldRunTasks, - logger - ); + const healthStatus = calculateHealthStatus(healthWithoutCapacity, config, shouldRunTasks, logger); + + const statusWithoutCapacity = healthStatus?.status; if (statusWithoutCapacity === HealthStatus.Warning) { logLevel = LogLevel.Warn; } else if (statusWithoutCapacity === HealthStatus.Error && !isEmpty(monitoredHealth.stats)) { diff --git a/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts b/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts index 88feea306050c..c710d304445e7 100644 --- a/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts +++ b/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts @@ -184,13 +184,14 @@ export function estimateCapacity( averageCapacityUsedByNonRecurringAndEphemeralTasksPerKibana + averageRecurringRequiredPerMinute / assumedKibanaInstances; - const status = getHealthStatus(logger, { + const { status, reason } = getHealthStatus(logger, { assumedRequiredThroughputPerMinutePerKibana, assumedAverageRecurringRequiredThroughputPerMinutePerKibana, capacityPerMinutePerKibana, }); return { status, + reason, timestamp: new Date().toISOString(), value: { observed: mapValues( @@ -231,27 +232,28 @@ interface GetHealthStatusParams { capacityPerMinutePerKibana: number; } -function getHealthStatus(logger: Logger, params: GetHealthStatusParams): HealthStatus { +function getHealthStatus( + logger: Logger, + params: GetHealthStatusParams +): { status: HealthStatus; reason?: string } { const { assumedRequiredThroughputPerMinutePerKibana, assumedAverageRecurringRequiredThroughputPerMinutePerKibana, capacityPerMinutePerKibana, } = params; if (assumedRequiredThroughputPerMinutePerKibana < capacityPerMinutePerKibana) { - return HealthStatus.OK; + return { status: HealthStatus.OK }; } if (assumedAverageRecurringRequiredThroughputPerMinutePerKibana < capacityPerMinutePerKibana) { - logger.debug( - `setting HealthStatus.Warning because assumedAverageRecurringRequiredThroughputPerMinutePerKibana (${assumedAverageRecurringRequiredThroughputPerMinutePerKibana}) < capacityPerMinutePerKibana (${capacityPerMinutePerKibana})` - ); - return HealthStatus.Warning; + const reason = `setting HealthStatus.Warning because assumedAverageRecurringRequiredThroughputPerMinutePerKibana (${assumedAverageRecurringRequiredThroughputPerMinutePerKibana}) < capacityPerMinutePerKibana (${capacityPerMinutePerKibana})`; + logger.warn(reason); + return { status: HealthStatus.Warning, reason }; } - logger.debug( - `setting HealthStatus.Error because assumedRequiredThroughputPerMinutePerKibana (${assumedRequiredThroughputPerMinutePerKibana}) >= capacityPerMinutePerKibana (${capacityPerMinutePerKibana}) AND assumedAverageRecurringRequiredThroughputPerMinutePerKibana (${assumedAverageRecurringRequiredThroughputPerMinutePerKibana}) >= capacityPerMinutePerKibana (${capacityPerMinutePerKibana})` - ); - return HealthStatus.Error; + const reason = `setting HealthStatus.Error because assumedRequiredThroughputPerMinutePerKibana (${assumedRequiredThroughputPerMinutePerKibana}) >= capacityPerMinutePerKibana (${capacityPerMinutePerKibana}) AND assumedAverageRecurringRequiredThroughputPerMinutePerKibana (${assumedAverageRecurringRequiredThroughputPerMinutePerKibana}) >= capacityPerMinutePerKibana (${capacityPerMinutePerKibana})`; + logger.warn(reason); + return { status: HealthStatus.Error, reason }; } export function withCapacityEstimate( diff --git a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts index 19485e41c2ae2..e192a72f7092d 100644 --- a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts +++ b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts @@ -68,6 +68,7 @@ export interface MonitoredStat { } export type RawMonitoredStat = MonitoredStat & { status: HealthStatus; + reason?: string; }; export interface RawMonitoringStats { diff --git a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.test.ts b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.test.ts index 4d69b23b699b7..0555f75fda278 100644 --- a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.test.ts @@ -375,24 +375,24 @@ describe('Task Run Statistics', () => { { Success: 40, RetryScheduled: 40, Failed: 20, status: 'OK' }, ]); - expect(logger.debug).toHaveBeenCalledTimes(5); - expect(logger.debug).toHaveBeenNthCalledWith( + expect(logger.warn).toHaveBeenCalledTimes(5); + expect(logger.warn).toHaveBeenNthCalledWith( 1, 'Health Status warn threshold has been exceeded, resultFrequencySummary.Failed (40) is greater than warn_threshold (39)' ); - expect(logger.debug).toHaveBeenNthCalledWith( + expect(logger.warn).toHaveBeenNthCalledWith( 2, 'Health Status error threshold has been exceeded, resultFrequencySummary.Failed (60) is greater than error_threshold (59)' ); - expect(logger.debug).toHaveBeenNthCalledWith( + expect(logger.warn).toHaveBeenNthCalledWith( 3, 'Health Status error threshold has been exceeded, resultFrequencySummary.Failed (60) is greater than error_threshold (59)' ); - expect(logger.debug).toHaveBeenNthCalledWith( + expect(logger.warn).toHaveBeenNthCalledWith( 4, 'Health Status error threshold has been exceeded, resultFrequencySummary.Failed (60) is greater than error_threshold (59)' ); - expect(logger.debug).toHaveBeenNthCalledWith( + expect(logger.warn).toHaveBeenNthCalledWith( 5, 'Health Status warn threshold has been exceeded, resultFrequencySummary.Failed (40) is greater than warn_threshold (39)' ); diff --git a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts index 0c6063af19286..8dca615f94190 100644 --- a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts +++ b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts @@ -433,11 +433,11 @@ function getHealthStatus( ): HealthStatus { if (resultFrequencySummary.Failed > executionErrorThreshold.warn_threshold) { if (resultFrequencySummary.Failed > executionErrorThreshold.error_threshold) { - logger.debug( + logger.warn( `Health Status error threshold has been exceeded, resultFrequencySummary.Failed (${resultFrequencySummary.Failed}) is greater than error_threshold (${executionErrorThreshold.error_threshold})` ); } else { - logger.debug( + logger.warn( `Health Status warn threshold has been exceeded, resultFrequencySummary.Failed (${resultFrequencySummary.Failed}) is greater than warn_threshold (${executionErrorThreshold.warn_threshold})` ); } diff --git a/x-pack/plugins/task_manager/server/routes/health.test.ts b/x-pack/plugins/task_manager/server/routes/health.test.ts index 6105b7487163c..c5b9add0ce294 100644 --- a/x-pack/plugins/task_manager/server/routes/health.test.ts +++ b/x-pack/plugins/task_manager/server/routes/health.test.ts @@ -15,15 +15,9 @@ import { mockHandlerArguments } from './_mock_handler_arguments'; import { sleep } from '../test_utils'; import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; -import { - HealthStatus, - MonitoringStats, - RawMonitoringStats, - summarizeMonitoringStats, -} from '../monitoring'; +import { MonitoringStats, RawMonitoringStats, summarizeMonitoringStats } from '../monitoring'; import { ServiceStatusLevels, Logger } from '@kbn/core/server'; import { configSchema, TaskManagerConfig } from '../config'; -import { calculateHealthStatusMock } from '../lib/calculate_health_status.mock'; import { FillPoolResult } from '../lib/fill_pool'; jest.mock('../lib/log_health_metrics', () => ({ @@ -191,8 +185,6 @@ describe('healthRoute', () => { it('logs the Task Manager stats at a fixed interval', async () => { const router = httpServiceMock.createRouter(); - const calculateHealthStatus = calculateHealthStatusMock.create(); - calculateHealthStatus.mockImplementation(() => HealthStatus.OK); const { logHealthMetrics } = jest.requireMock('../lib/log_health_metrics'); const mockStat = mockHealthStats(); @@ -253,8 +245,6 @@ describe('healthRoute', () => { it(`logs at a warn level if the status is warning`, async () => { const router = httpServiceMock.createRouter(); - const calculateHealthStatus = calculateHealthStatusMock.create(); - calculateHealthStatus.mockImplementation(() => HealthStatus.Warning); const { logHealthMetrics } = jest.requireMock('../lib/log_health_metrics'); const warnRuntimeStat = mockHealthStats(); @@ -265,7 +255,7 @@ describe('healthRoute', () => { const stats$ = new Subject(); const id = uuidv4(); - healthRoute({ + const { serviceStatus$ } = healthRoute({ router, monitoringStats$: stats$, logger, @@ -287,6 +277,8 @@ describe('healthRoute', () => { docLinks, }); + const serviceStatus = getLatest(serviceStatus$); + stats$.next(warnRuntimeStat); await sleep(1001); stats$.next(warnConfigurationStat); @@ -295,6 +287,12 @@ describe('healthRoute', () => { await sleep(1001); stats$.next(warnEphemeralStat); + expect(await serviceStatus).toMatchObject({ + level: ServiceStatusLevels.degraded, + summary: + 'Task Manager is unhealthy - Reason: setting HealthStatus.Warning because assumedAverageRecurringRequiredThroughputPerMinutePerKibana (78.28472222222223) < capacityPerMinutePerKibana (200)', + }); + expect(logHealthMetrics).toBeCalledTimes(4); expect(logHealthMetrics.mock.calls[0][0]).toMatchObject({ id, @@ -332,8 +330,6 @@ describe('healthRoute', () => { it(`logs at an error level if the status is error`, async () => { const router = httpServiceMock.createRouter(); - const calculateHealthStatus = calculateHealthStatusMock.create(); - calculateHealthStatus.mockImplementation(() => HealthStatus.Error); const { logHealthMetrics } = jest.requireMock('../lib/log_health_metrics'); const errorRuntimeStat = mockHealthStats(); @@ -344,7 +340,7 @@ describe('healthRoute', () => { const stats$ = new Subject(); const id = uuidv4(); - healthRoute({ + const { serviceStatus$ } = healthRoute({ router, monitoringStats$: stats$, logger, @@ -366,6 +362,8 @@ describe('healthRoute', () => { docLinks, }); + const serviceStatus = getLatest(serviceStatus$); + stats$.next(errorRuntimeStat); await sleep(1001); stats$.next(errorConfigurationStat); @@ -374,6 +372,12 @@ describe('healthRoute', () => { await sleep(1001); stats$.next(errorEphemeralStat); + expect(await serviceStatus).toMatchObject({ + level: ServiceStatusLevels.degraded, + summary: + 'Task Manager is unhealthy - Reason: setting HealthStatus.Warning because assumedAverageRecurringRequiredThroughputPerMinutePerKibana (78.28472222222223) < capacityPerMinutePerKibana (200)', + }); + expect(logHealthMetrics).toBeCalledTimes(4); expect(logHealthMetrics.mock.calls[0][0]).toMatchObject({ id, @@ -481,12 +485,13 @@ describe('healthRoute', () => { expect(await serviceStatus).toMatchObject({ level: ServiceStatusLevels.degraded, - summary: 'Task Manager is unhealthy', + summary: + 'Task Manager is unhealthy - Reason: setting HealthStatus.Error because of expired hot timestamps', }); - const debugCalls = (logger as jest.Mocked).debug.mock.calls as string[][]; + const warnCalls = (logger as jest.Mocked).warn.mock.calls as string[][]; const warnMessage = /^setting HealthStatus.Warning because assumedAverageRecurringRequiredThroughputPerMinutePerKibana/; - const found = debugCalls + const found = warnCalls .map((arr) => arr[0]) .find((message) => message.match(warnMessage) != null); expect(found).toMatch(warnMessage); @@ -497,7 +502,7 @@ describe('healthRoute', () => { const stats$ = new Subject(); - healthRoute({ + const { serviceStatus$ } = healthRoute({ router, monitoringStats$: stats$, logger, @@ -514,6 +519,8 @@ describe('healthRoute', () => { docLinks, }); + const serviceStatus = getLatest(serviceStatus$); + await sleep(0); const lastUpdateOfWorkload = new Date(Date.now() - 120000).toISOString(); @@ -533,6 +540,11 @@ describe('healthRoute', () => { await sleep(2000); + expect(await serviceStatus).toMatchObject({ + level: ServiceStatusLevels.degraded, + summary: + 'Task Manager is unhealthy - Reason: setting HealthStatus.Error because of expired cold timestamps', + }); expect(await handler(context, req, res)).toMatchObject({ body: { status: 'error', @@ -572,7 +584,7 @@ describe('healthRoute', () => { const router = httpServiceMock.createRouter(); const stats$ = new Subject(); - healthRoute({ + const { serviceStatus$ } = healthRoute({ router, monitoringStats$: stats$, logger, @@ -588,7 +600,7 @@ describe('healthRoute', () => { shouldRunTasks: true, docLinks, }); - + const serviceStatus = getLatest(serviceStatus$); await sleep(0); // eslint-disable-next-line @typescript-eslint/naming-convention @@ -611,6 +623,11 @@ describe('healthRoute', () => { const [context, req, res] = mockHandlerArguments({}, {}, ['ok']); + expect(await serviceStatus).toMatchObject({ + level: ServiceStatusLevels.degraded, + summary: + 'Task Manager is unhealthy - Reason: setting HealthStatus.Error because of expired hot timestamps', + }); expect(await handler(context, req, res)).toMatchObject({ body: { status: 'error', diff --git a/x-pack/plugins/task_manager/server/routes/health.ts b/x-pack/plugins/task_manager/server/routes/health.ts index 9ae3cee2bdcf5..7a348147b0b76 100644 --- a/x-pack/plugins/task_manager/server/routes/health.ts +++ b/x-pack/plugins/task_manager/server/routes/health.ts @@ -30,6 +30,7 @@ import { calculateHealthStatus } from '../lib/calculate_health_status'; export type MonitoredHealth = RawMonitoringStats & { id: string; + reason?: string; status: HealthStatus; timestamp: string; }; @@ -87,10 +88,15 @@ export function healthRoute(params: HealthRouteParams): { function getHealthStatus(monitoredStats: MonitoringStats) { const summarizedStats = summarizeMonitoringStats(logger, monitoredStats, config); - const status = calculateHealthStatus(summarizedStats, config, shouldRunTasks, logger); + const { status, reason } = calculateHealthStatus( + summarizedStats, + config, + shouldRunTasks, + logger + ); const now = Date.now(); const timestamp = new Date(now).toISOString(); - return { id: taskManagerId, timestamp, status, ...summarizedStats }; + return { id: taskManagerId, timestamp, status, reason, ...summarizedStats }; } const serviceStatus$: Subject = new Subject(); @@ -106,7 +112,7 @@ export function healthRoute(params: HealthRouteParams): { tap((stats) => { lastMonitoredStats = stats; }), - // Only calculate the summerized stats (calculates all runnign averages and evaluates state) + // Only calculate the summarized stats (calculates all running averages and evaluates state) // when needed by throttling down to the requiredHotStatsFreshness map((stats) => withServiceStatus(getHealthStatus(stats))) ) @@ -174,15 +180,19 @@ export function healthRoute(params: HealthRouteParams): { export function withServiceStatus( monitoredHealth: MonitoredHealth ): [MonitoredHealth, TaskManagerServiceStatus] { + const { reason, status } = monitoredHealth; + const level = - monitoredHealth.status === HealthStatus.OK - ? ServiceStatusLevels.available - : ServiceStatusLevels.degraded; + status === HealthStatus.OK ? ServiceStatusLevels.available : ServiceStatusLevels.degraded; + + const defaultMessage = LEVEL_SUMMARY[level.toString()]; + const summary = reason ? `${defaultMessage} - Reason: ${reason}` : defaultMessage; + return [ monitoredHealth, { level, - summary: LEVEL_SUMMARY[level.toString()], + summary, }, ]; } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 3756275f49b70..21137b5344e2e 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -20422,7 +20422,6 @@ "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "Changer de calque", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "Annuler", "xpack.maps.aggs.defaultCountLabel": "compte", - "xpack.maps.appTitle": "Cartes", "xpack.maps.attribution.addBtnAriaLabel": "Ajouter une attribution", "xpack.maps.attribution.addBtnLabel": "Ajouter une attribution", "xpack.maps.attribution.applyBtnLabel": "Appliquer", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f209a10ee17fa..74b4c94ef5d1c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -20422,7 +20422,6 @@ "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "レイヤーを変更", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "キャンセル", "xpack.maps.aggs.defaultCountLabel": "カウント", - "xpack.maps.appTitle": "マップ", "xpack.maps.attribution.addBtnAriaLabel": "属性を追加", "xpack.maps.attribution.addBtnLabel": "属性を追加", "xpack.maps.attribution.applyBtnLabel": "適用", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 8543c48afec38..d78366da8b8c7 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -20422,7 +20422,6 @@ "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "更改图层", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "取消", "xpack.maps.aggs.defaultCountLabel": "计数", - "xpack.maps.appTitle": "Maps", "xpack.maps.attribution.addBtnAriaLabel": "添加归因", "xpack.maps.attribution.addBtnLabel": "添加归因", "xpack.maps.attribution.applyBtnLabel": "应用", diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts index 2efd2bf52309b..5d19932090168 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts @@ -33,23 +33,33 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'Actions', ], [ - 'Host-9qenwrl9ko', + 'Host-dpu1a2r2yi', 'x', 'x', 'Warning', + 'macOS', + '10.2.17.24, 10.56.215.200,10.254.196.130', + 'x', + 'x', + '', + ], + [ + 'Host-rs9wp4o6l9', + 'x', + 'x', + 'Success', 'Linux', - '10.56.228.101, 10.201.120.140,10.236.180.146', + '10.138.79.131, 10.170.160.154', 'x', 'x', '', ], - ['Host-qw2bti801m', 'x', 'x', 'Failure', 'Linux', '10.244.59.227', 'x', 'x', ''], [ 'Host-u5jy6j0pwb', 'x', 'x', 'Warning', - 'Linux', + 'macOS', '10.87.11.145, 10.117.106.109,10.242.136.97', 'x', 'x', @@ -71,9 +81,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { return tableData; }; - // Failing: See https://github.com/elastic/kibana/issues/154917 - // Failing: See https://github.com/elastic/kibana/issues/154916 - describe.skip('endpoint list', function () { + describe('endpoint list', function () { const sleep = (ms = 100) => new Promise((resolve) => setTimeout(resolve, ms)); let indexedData: IndexedHostsAndAlertsResponse; describe('when initially navigating to page', () => {