From 9c955577f108e92b4401d4c331001bdc2230f2f1 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 30 Jul 2020 11:21:22 +0300 Subject: [PATCH 01/28] Navigate from discover to visualize with registering into a trigger --- .../sidebar/discover_field_details.tsx | 11 +- .../discover/public/kibana_services.ts | 5 + src/plugins/discover/public/plugin.ts | 3 + src/plugins/ui_actions/public/index.ts | 2 + src/plugins/ui_actions/public/plugin.ts | 8 +- .../ui_actions/public/triggers/index.ts | 1 + .../triggers/visualize_field_trigger.ts | 27 +++++ src/plugins/ui_actions/public/types.ts | 8 +- .../visualizations/common/constants.ts | 1 + .../public/actions/visualize_field_action.ts | 102 ++++++++++++++++++ src/plugins/visualizations/public/plugin.ts | 14 ++- 11 files changed, 176 insertions(+), 6 deletions(-) create mode 100644 src/plugins/ui_actions/public/triggers/visualize_field_trigger.ts create mode 100644 src/plugins/visualizations/public/actions/visualize_field_action.ts diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx index dd95a45f71626..9574a2b17a997 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx @@ -19,10 +19,11 @@ import React from 'react'; import { EuiLink, EuiIconTip, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { VISUALIZE_FIELD_TRIGGER } from '../../../../../ui_actions/public'; import { DiscoverFieldBucket } from './discover_field_bucket'; import { getWarnings } from './lib/get_warnings'; import { Bucket, FieldDetails } from './types'; -import { getServices } from '../../../kibana_services'; +import { getServices, getUiActions } from '../../../kibana_services'; import { IndexPatternField, IndexPattern } from '../../../../../data/public'; interface DiscoverFieldDetailsProps { @@ -80,8 +81,12 @@ export function DiscoverFieldDetails({ <> { - getServices().core.application.navigateToApp(details.visualizeUrl.app, { - path: details.visualizeUrl.path, + // getServices().core.application.navigateToApp(details.visualizeUrl.app, { + // path: details.visualizeUrl.path, + // }); + getUiActions().executeTriggerActions(VISUALIZE_FIELD_TRIGGER, { + indexPatternId: indexPattern.id, + fieldName: field.name, }); }} className="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall" diff --git a/src/plugins/discover/public/kibana_services.ts b/src/plugins/discover/public/kibana_services.ts index ecb5d7fd90283..bc25fa71dcf41 100644 --- a/src/plugins/discover/public/kibana_services.ts +++ b/src/plugins/discover/public/kibana_services.ts @@ -20,6 +20,7 @@ import _ from 'lodash'; import { createHashHistory } from 'history'; import { ScopedHistory } from 'kibana/public'; +import { UiActionsStart } from 'src/plugins/ui_actions/public'; import { DiscoverServices } from './build_services'; import { createGetterSetter } from '../../kibana_utils/public'; import { search } from '../../data/public'; @@ -27,6 +28,7 @@ import { DocViewsRegistry } from './application/doc_views/doc_views_registry'; let angularModule: any = null; let services: DiscoverServices | null = null; +let uiActions: UiActionsStart; /** * set bootstrapped inner angular module @@ -53,6 +55,9 @@ export function setServices(newServices: any) { services = newServices; } +export const setUiActions = (pluginUiActions: UiActionsStart) => (uiActions = pluginUiActions); +export const getUiActions = () => uiActions; + export const [getUrlTracker, setUrlTracker] = createGetterSetter<{ setTrackedUrl: (url: string) => void; restorePreviousUrl: () => void; diff --git a/src/plugins/discover/public/plugin.ts b/src/plugins/discover/public/plugin.ts index 20e13d204e0e9..015f4267646c1 100644 --- a/src/plugins/discover/public/plugin.ts +++ b/src/plugins/discover/public/plugin.ts @@ -53,6 +53,7 @@ import { setUrlTracker, setAngularModule, setServices, + setUiActions, setScopedHistory, getScopedHistory, syncHistoryLocations, @@ -314,6 +315,8 @@ export class DiscoverPlugin this.innerAngularInitialized = true; }; + setUiActions(plugins.uiActions); + this.initializeServices = async () => { if (this.servicesInitialized) { return { core, plugins }; diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index a9b413fb36542..620a6dc63a2d0 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -43,6 +43,8 @@ export { valueClickTrigger, APPLY_FILTER_TRIGGER, applyFilterTrigger, + VISUALIZE_FIELD_TRIGGER, + visualizeFieldTrigger, } from './triggers'; export { TriggerContextMapping, TriggerId, ActionContextMapping, ActionType } from './types'; export { ActionByType } from './actions'; diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index 71148656cbb16..92a36aaafe6b6 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -19,7 +19,12 @@ import { CoreStart, CoreSetup, Plugin, PluginInitializerContext } from 'src/core/public'; import { UiActionsService } from './service'; -import { selectRangeTrigger, valueClickTrigger, applyFilterTrigger } from './triggers'; +import { + selectRangeTrigger, + valueClickTrigger, + applyFilterTrigger, + visualizeFieldTrigger, +} from './triggers'; export type UiActionsSetup = Pick< UiActionsService, @@ -42,6 +47,7 @@ export class UiActionsPlugin implements Plugin { this.service.registerTrigger(selectRangeTrigger); this.service.registerTrigger(valueClickTrigger); this.service.registerTrigger(applyFilterTrigger); + this.service.registerTrigger(visualizeFieldTrigger); return this.service; } diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts index a5bf9e1822941..00ca22d9a8431 100644 --- a/src/plugins/ui_actions/public/triggers/index.ts +++ b/src/plugins/ui_actions/public/triggers/index.ts @@ -23,3 +23,4 @@ export * from './trigger_internal'; export * from './select_range_trigger'; export * from './value_click_trigger'; export * from './apply_filter_trigger'; +export * from './visualize_field_trigger'; diff --git a/src/plugins/ui_actions/public/triggers/visualize_field_trigger.ts b/src/plugins/ui_actions/public/triggers/visualize_field_trigger.ts new file mode 100644 index 0000000000000..4f3c5f613eddf --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/visualize_field_trigger.ts @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Trigger } from '.'; + +export const VISUALIZE_FIELD_TRIGGER = 'VISUALIZE_FIELD_TRIGGER'; +export const visualizeFieldTrigger: Trigger<'VISUALIZE_FIELD_TRIGGER'> = { + id: VISUALIZE_FIELD_TRIGGER, + title: 'Visualize field', + description: 'Triggered when user wants to visualize a field.', +}; diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index 5631441cf9a1b..7c5b5d0a7c36f 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -19,7 +19,12 @@ import { ActionInternal } from './actions/action_internal'; import { TriggerInternal } from './triggers/trigger_internal'; -import { SELECT_RANGE_TRIGGER, VALUE_CLICK_TRIGGER, APPLY_FILTER_TRIGGER } from './triggers'; +import { + SELECT_RANGE_TRIGGER, + VALUE_CLICK_TRIGGER, + APPLY_FILTER_TRIGGER, + VISUALIZE_FIELD_TRIGGER, +} from './triggers'; import type { RangeSelectContext, ValueClickContext } from '../../embeddable/public'; import type { ApplyGlobalFilterActionContext } from '../../data/public'; @@ -39,6 +44,7 @@ export interface TriggerContextMapping { [SELECT_RANGE_TRIGGER]: RangeSelectContext; [VALUE_CLICK_TRIGGER]: ValueClickContext; [APPLY_FILTER_TRIGGER]: ApplyGlobalFilterActionContext; + [VISUALIZE_FIELD_TRIGGER]: any; } const DEFAULT_ACTION = ''; diff --git a/src/plugins/visualizations/common/constants.ts b/src/plugins/visualizations/common/constants.ts index 9129f060c5eef..bbb70c652aa31 100644 --- a/src/plugins/visualizations/common/constants.ts +++ b/src/plugins/visualizations/common/constants.ts @@ -18,3 +18,4 @@ */ export const VISUALIZE_ENABLE_LABS_SETTING = 'visualize:enableLabs'; +export const AGGS_TERMS_SIZE_SETTING = 'discover:aggs:terms:size'; diff --git a/src/plugins/visualizations/public/actions/visualize_field_action.ts b/src/plugins/visualizations/public/actions/visualize_field_action.ts new file mode 100644 index 0000000000000..67e0ac5ac4d93 --- /dev/null +++ b/src/plugins/visualizations/public/actions/visualize_field_action.ts @@ -0,0 +1,102 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import rison from 'rison-node'; +import { stringify } from 'query-string'; +import { createAction } from '../../../ui_actions/public'; +import { getApplication, getUISettings, getIndexPatterns } from '../services'; +import { AGGS_TERMS_SIZE_SETTING } from '../../common/constants'; +import { KBN_FIELD_TYPES } from '../../../data/public'; +// import { VisualizeConstants } from '../application/visualize_constants'; + +export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD'; + +export interface VisualizeFieldContext { + fieldName: string; + indexPatternId: string; +} + +export const visualizeFieldAction = createAction({ + type: ACTION_VISUALIZE_FIELD, + getDisplayName: () => 'Visualize Field', + execute: async (context) => { + const indexPattern = await getIndexPatterns().get(context.indexPatternId); + const field = indexPattern.fields.find((fld) => fld.name === context.fieldName); + const aggsTermSize = getUISettings().get(AGGS_TERMS_SIZE_SETTING); + const isGeoPoint = field?.type === KBN_FIELD_TYPES.GEO_POINT; + const type = isGeoPoint ? 'tile_map' : 'histogram'; + let agg; + + // If we're visualizing a date field, and our index is time based (and thus has a time filter), + // then run a date histogram + if (field?.type === 'date' && indexPattern.timeFieldName === context.fieldName) { + agg = { + type: 'date_histogram', + schema: 'segment', + params: { + field: context.fieldName, + interval: 'auto', + }, + }; + } else if (isGeoPoint) { + agg = { + type: 'geohash_grid', + schema: 'segment', + params: { + field: context.fieldName, + precision: 3, + }, + }; + } else { + agg = { + type: 'terms', + schema: 'segment', + params: { + field: context.fieldName, + size: parseInt(aggsTermSize, 10), + orderBy: '1', + }, + }; + } + + const linkUrlParams = { + indexPattern: context.indexPatternId, + type, + _a: rison.encode({ + vis: { + type, + aggs: [{ schema: 'metric', type: 'count', id: '1' }, agg], + }, + } as any), + }; + + getApplication().navigateToApp('visualize', { + path: `#/create?${stringify(linkUrlParams)}`, + }); + }, +}); + +// export const createVisualizeFieldAction = (application) => +// createAction({ +// type: ACTION_VISUALIZE_FIELD, +// getDisplayName: () => 'Visualize Field', +// execute: async (context) => { +// application.navigateToApp('visualize', { path: VisualizeConstants.LANDING_PAGE_PATH }); +// // console.log(context.field); +// }, +// }); diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 3546fa4056491..d18cad527c45c 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -70,7 +70,12 @@ import { createStartServicesGetter, StartServicesGetter } from '../../kibana_uti import { createSavedVisLoader, SavedVisualizationsLoader } from './saved_visualizations'; import { SerializedVis, Vis } from './vis'; import { showNewVisModal } from './wizard'; -import { UiActionsStart } from '../../ui_actions/public'; +import { UiActionsStart, VISUALIZE_FIELD_TRIGGER } from '../../ui_actions/public'; +import { + visualizeFieldAction, + VisualizeFieldContext, + ACTION_VISUALIZE_FIELD, +} from './actions/visualize_field_action'; import { convertFromSerializedVis, convertToSerializedVis, @@ -111,6 +116,12 @@ export interface VisualizationsStartDeps { application: ApplicationStart; } +declare module '../../ui_actions/public' { + export interface ActionContextMapping { + [ACTION_VISUALIZE_FIELD]: VisualizeFieldContext; + } +} + /** * Visualizations Plugin - public * @@ -191,6 +202,7 @@ export class VisualizationsPlugin overlays: core.overlays, }); setSavedSearchLoader(savedSearchLoader); + uiActions.addTriggerAction(VISUALIZE_FIELD_TRIGGER, visualizeFieldAction); return { ...types, showNewVisModal, From 08fcf5b22598160e55cd942687cf80fb80e2239d Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 30 Jul 2020 17:01:06 +0300 Subject: [PATCH 02/28] Implement the VISUALIZE_FIELD action --- .../visualizations/common/constants.ts | 1 - src/plugins/visualizations/public/plugin.ts | 14 +-------- src/plugins/visualize/common/constants.ts | 21 +++++++++++++ src/plugins/visualize/kibana.json | 3 +- .../public/actions/visualize_field_action.ts | 27 ++--------------- src/plugins/visualize/public/plugin.ts | 24 +++++++++++++-- src/plugins/visualize/public/services.ts | 30 +++++++++++++++++++ 7 files changed, 78 insertions(+), 42 deletions(-) create mode 100644 src/plugins/visualize/common/constants.ts rename src/plugins/{visualizations => visualize}/public/actions/visualize_field_action.ts (75%) create mode 100644 src/plugins/visualize/public/services.ts diff --git a/src/plugins/visualizations/common/constants.ts b/src/plugins/visualizations/common/constants.ts index bbb70c652aa31..9129f060c5eef 100644 --- a/src/plugins/visualizations/common/constants.ts +++ b/src/plugins/visualizations/common/constants.ts @@ -18,4 +18,3 @@ */ export const VISUALIZE_ENABLE_LABS_SETTING = 'visualize:enableLabs'; -export const AGGS_TERMS_SIZE_SETTING = 'discover:aggs:terms:size'; diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index d18cad527c45c..3546fa4056491 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -70,12 +70,7 @@ import { createStartServicesGetter, StartServicesGetter } from '../../kibana_uti import { createSavedVisLoader, SavedVisualizationsLoader } from './saved_visualizations'; import { SerializedVis, Vis } from './vis'; import { showNewVisModal } from './wizard'; -import { UiActionsStart, VISUALIZE_FIELD_TRIGGER } from '../../ui_actions/public'; -import { - visualizeFieldAction, - VisualizeFieldContext, - ACTION_VISUALIZE_FIELD, -} from './actions/visualize_field_action'; +import { UiActionsStart } from '../../ui_actions/public'; import { convertFromSerializedVis, convertToSerializedVis, @@ -116,12 +111,6 @@ export interface VisualizationsStartDeps { application: ApplicationStart; } -declare module '../../ui_actions/public' { - export interface ActionContextMapping { - [ACTION_VISUALIZE_FIELD]: VisualizeFieldContext; - } -} - /** * Visualizations Plugin - public * @@ -202,7 +191,6 @@ export class VisualizationsPlugin overlays: core.overlays, }); setSavedSearchLoader(savedSearchLoader); - uiActions.addTriggerAction(VISUALIZE_FIELD_TRIGGER, visualizeFieldAction); return { ...types, showNewVisModal, diff --git a/src/plugins/visualize/common/constants.ts b/src/plugins/visualize/common/constants.ts new file mode 100644 index 0000000000000..bbb70c652aa31 --- /dev/null +++ b/src/plugins/visualize/common/constants.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const VISUALIZE_ENABLE_LABS_SETTING = 'visualize:enableLabs'; +export const AGGS_TERMS_SIZE_SETTING = 'discover:aggs:terms:size'; diff --git a/src/plugins/visualize/kibana.json b/src/plugins/visualize/kibana.json index 520d1e1daa6fe..a6cc8d8f8af60 100644 --- a/src/plugins/visualize/kibana.json +++ b/src/plugins/visualize/kibana.json @@ -9,7 +9,8 @@ "navigation", "savedObjects", "visualizations", - "embeddable" + "embeddable", + "uiActions" ], "optionalPlugins": ["home", "share"], "requiredBundles": [ diff --git a/src/plugins/visualizations/public/actions/visualize_field_action.ts b/src/plugins/visualize/public/actions/visualize_field_action.ts similarity index 75% rename from src/plugins/visualizations/public/actions/visualize_field_action.ts rename to src/plugins/visualize/public/actions/visualize_field_action.ts index 67e0ac5ac4d93..121127ddf4ba9 100644 --- a/src/plugins/visualizations/public/actions/visualize_field_action.ts +++ b/src/plugins/visualize/public/actions/visualize_field_action.ts @@ -21,8 +21,6 @@ import { stringify } from 'query-string'; import { createAction } from '../../../ui_actions/public'; import { getApplication, getUISettings, getIndexPatterns } from '../services'; import { AGGS_TERMS_SIZE_SETTING } from '../../common/constants'; -import { KBN_FIELD_TYPES } from '../../../data/public'; -// import { VisualizeConstants } from '../application/visualize_constants'; export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD'; @@ -38,8 +36,6 @@ export const visualizeFieldAction = createAction( const indexPattern = await getIndexPatterns().get(context.indexPatternId); const field = indexPattern.fields.find((fld) => fld.name === context.fieldName); const aggsTermSize = getUISettings().get(AGGS_TERMS_SIZE_SETTING); - const isGeoPoint = field?.type === KBN_FIELD_TYPES.GEO_POINT; - const type = isGeoPoint ? 'tile_map' : 'histogram'; let agg; // If we're visualizing a date field, and our index is time based (and thus has a time filter), @@ -53,15 +49,6 @@ export const visualizeFieldAction = createAction( interval: 'auto', }, }; - } else if (isGeoPoint) { - agg = { - type: 'geohash_grid', - schema: 'segment', - params: { - field: context.fieldName, - precision: 3, - }, - }; } else { agg = { type: 'terms', @@ -76,10 +63,10 @@ export const visualizeFieldAction = createAction( const linkUrlParams = { indexPattern: context.indexPatternId, - type, + type: 'histogram', _a: rison.encode({ vis: { - type, + type: 'histogram', aggs: [{ schema: 'metric', type: 'count', id: '1' }, agg], }, } as any), @@ -90,13 +77,3 @@ export const visualizeFieldAction = createAction( }); }, }); - -// export const createVisualizeFieldAction = (application) => -// createAction({ -// type: ACTION_VISUALIZE_FIELD, -// getDisplayName: () => 'Visualize Field', -// execute: async (context) => { -// application.navigateToApp('visualize', { path: VisualizeConstants.LANDING_PAGE_PATH }); -// // console.log(context.field); -// }, -// }); diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index fd9a67599414f..d08f760c85dcf 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -43,6 +43,13 @@ import { VisualizeServices } from './application/types'; import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; import { SavedObjectsStart } from '../../saved_objects/public'; import { EmbeddableStart } from '../../embeddable/public'; +import { UiActionsStart, VISUALIZE_FIELD_TRIGGER } from '../../ui_actions/public'; +import { setUISettings, setApplication, setIndexPatterns } from './services'; +import { + visualizeFieldAction, + VisualizeFieldContext, + ACTION_VISUALIZE_FIELD, +} from './actions/visualize_field_action'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; @@ -52,6 +59,7 @@ export interface VisualizePluginStartDependencies { embeddable: EmbeddableStart; kibanaLegacy: KibanaLegacyStart; savedObjects: SavedObjectsStart; + uiActions: UiActionsStart; } export interface VisualizePluginSetupDependencies { @@ -60,6 +68,12 @@ export interface VisualizePluginSetupDependencies { data: DataPublicPluginSetup; } +declare module '../../ui_actions/public' { + export interface ActionContextMapping { + [ACTION_VISUALIZE_FIELD]: VisualizeFieldContext; + } +} + export interface FeatureFlagConfig { showNewVisualizeFlow: boolean; } @@ -109,6 +123,8 @@ export class VisualizePlugin stopUrlTracker(); }; + setUISettings(core.uiSettings); + core.application.register({ id: 'visualize', title: 'Visualize', @@ -135,7 +151,6 @@ export class VisualizePlugin const unlistenParentHistory = params.history.listen(() => { window.dispatchEvent(new HashChangeEvent('hashchange')); }); - /** * current implementation uses 2 history objects: * 1. the hash history (used for the react hash router) @@ -201,7 +216,12 @@ export class VisualizePlugin } } - public start(core: CoreStart, plugins: VisualizePluginStartDependencies) {} + public start(core: CoreStart, plugins: VisualizePluginStartDependencies) { + setApplication(core.application); + setIndexPatterns(plugins.data.indexPatterns); + // const visualizeFieldAction = createVisualizeFieldAction(core.application); + plugins.uiActions.addTriggerAction(VISUALIZE_FIELD_TRIGGER, visualizeFieldAction); + } stop() { if (this.stopUrlTracking) { diff --git a/src/plugins/visualize/public/services.ts b/src/plugins/visualize/public/services.ts new file mode 100644 index 0000000000000..e51b4be7fb693 --- /dev/null +++ b/src/plugins/visualize/public/services.ts @@ -0,0 +1,30 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ApplicationStart, IUiSettingsClient } from '../../../core/public'; +import { createGetterSetter } from '../../../plugins/kibana_utils/public'; +import { IndexPatternsContract } from '../../../plugins/data/public'; + +export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); + +export const [getApplication, setApplication] = createGetterSetter('Application'); + +export const [getIndexPatterns, setIndexPatterns] = createGetterSetter( + 'IndexPatterns' +); From a3756ff098e96862dc78af89911b341a91efb24c Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 31 Jul 2020 14:29:04 +0300 Subject: [PATCH 03/28] Implementation of the maps app trigger actions with the isCompatible functionality --- .../sidebar/discover_field_details.tsx | 23 ++++-- .../components/sidebar/lib/get_details.ts | 1 + .../sidebar/lib/get_visualize_trigger.ts | 77 +++++++++++++++++++ .../application/components/sidebar/types.ts | 1 + src/plugins/ui_actions/public/index.ts | 11 ++- src/plugins/ui_actions/public/plugin.ts | 2 + .../ui_actions/public/triggers/index.ts | 1 + .../triggers/visualize_geo_field_trigger.ts | 27 +++++++ src/plugins/ui_actions/public/types.ts | 15 +++- .../public/actions/visualize_field_action.ts | 5 -- src/plugins/visualize/public/plugin.ts | 10 +-- x-pack/plugins/maps/public/actions/index.ts | 1 + .../actions/visualize_geo_field_action.ts | 48 ++++++++++++ .../plugins/maps/public/kibana_services.d.ts | 3 + x-pack/plugins/maps/public/kibana_services.js | 4 + x-pack/plugins/maps/public/plugin.ts | 15 ++++ 16 files changed, 224 insertions(+), 20 deletions(-) create mode 100644 src/plugins/discover/public/application/components/sidebar/lib/get_visualize_trigger.ts create mode 100644 src/plugins/ui_actions/public/triggers/visualize_geo_field_trigger.ts create mode 100644 x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx index 9574a2b17a997..cdbb0023a878f 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx @@ -16,14 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; +import React, { useState } from 'react'; import { EuiLink, EuiIconTip, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VISUALIZE_FIELD_TRIGGER } from '../../../../../ui_actions/public'; import { DiscoverFieldBucket } from './discover_field_bucket'; import { getWarnings } from './lib/get_warnings'; +import { triggerVisualizeActions, isFieldVisualizable } from './lib/get_visualize_trigger'; import { Bucket, FieldDetails } from './types'; -import { getServices, getUiActions } from '../../../kibana_services'; +import { getServices } from '../../../kibana_services'; import { IndexPatternField, IndexPattern } from '../../../../../data/public'; interface DiscoverFieldDetailsProps { @@ -40,6 +40,16 @@ export function DiscoverFieldDetails({ onAddFilter, }: DiscoverFieldDetailsProps) { const warnings = getWarnings(field); + const [showVisualizeLink, setShowVisualizeLink] = useState(false); + + isFieldVisualizable(field, indexPattern.id, details.columns).then( + (v) => { + setShowVisualizeLink(v); + }, + () => { + setShowVisualizeLink(false); + } + ); return (
@@ -77,17 +87,14 @@ export function DiscoverFieldDetails({
)} - {details.visualizeUrl && ( + {showVisualizeLink && ( <> { // getServices().core.application.navigateToApp(details.visualizeUrl.app, { // path: details.visualizeUrl.path, // }); - getUiActions().executeTriggerActions(VISUALIZE_FIELD_TRIGGER, { - indexPatternId: indexPattern.id, - fieldName: field.name, - }); + triggerVisualizeActions(field, indexPattern.id, details.columns); }} className="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall" data-test-subj={`fieldVisualize-${field.name}`} diff --git a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts index 7ac9f009d73d5..9ea8cdfbb60fe 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts @@ -42,6 +42,7 @@ export function getDetails( count: 5, grouped: false, }), + columns, }; if (details.buckets) { for (const bucket of details.buckets) { diff --git a/src/plugins/discover/public/application/components/sidebar/lib/get_visualize_trigger.ts b/src/plugins/discover/public/application/components/sidebar/lib/get_visualize_trigger.ts new file mode 100644 index 0000000000000..b815d1e98c28c --- /dev/null +++ b/src/plugins/discover/public/application/components/sidebar/lib/get_visualize_trigger.ts @@ -0,0 +1,77 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { + VISUALIZE_FIELD_TRIGGER, + VISUALIZE_GEO_FIELD_TRIGGER, +} from '../../../../../../ui_actions/public'; +import { getUiActions } from '../../../../kibana_services'; +import { IndexPatternField, KBN_FIELD_TYPES } from '../../../../../../data/public'; + +function getTrigger(type: string) { + return type === KBN_FIELD_TYPES.GEO_POINT ? VISUALIZE_GEO_FIELD_TRIGGER : VISUALIZE_FIELD_TRIGGER; +} + +async function getCompatibleActions( + fieldName: string, + indexPatternId: string, + tooltipProperties: string[], + action: typeof VISUALIZE_FIELD_TRIGGER | typeof VISUALIZE_GEO_FIELD_TRIGGER +) { + const compatibleActions = await getUiActions().getTriggerCompatibleActions(action, { + indexPatternId, + fieldName, + tooltipProperties, + }); + return compatibleActions.length; +} + +export function triggerVisualizeActions( + field: IndexPatternField, + indexPatternId: string | undefined, + tooltipProperties: string[] +) { + if (!indexPatternId) return; + const trigger = getTrigger(field.type); + const triggerOptions = { + indexPatternId, + fieldName: field.name, + tooltipProperties: trigger === VISUALIZE_GEO_FIELD_TRIGGER ? tooltipProperties : [], + }; + getUiActions().executeTriggerActions(trigger, triggerOptions); +} + +export async function isFieldVisualizable( + field: IndexPatternField, + indexPatternId: string | undefined, + tooltipProperties: string[] +) { + if (!indexPatternId) return; + if (field.name === '_id') { + // Else you'd get a 'Fielddata access on the _id field is disallowed' error on ES side. + return false; + } + const trigger = getTrigger(field.type); + const compatibleActions = await getCompatibleActions( + field.name, + indexPatternId, + tooltipProperties, + trigger + ); + return compatibleActions > 0 && field.visualizable; +} diff --git a/src/plugins/discover/public/application/components/sidebar/types.ts b/src/plugins/discover/public/application/components/sidebar/types.ts index e86138761c747..96a15639b49d4 100644 --- a/src/plugins/discover/public/application/components/sidebar/types.ts +++ b/src/plugins/discover/public/application/components/sidebar/types.ts @@ -27,6 +27,7 @@ export interface FieldDetails { exists: number; total: boolean; buckets: Bucket[]; + columns: string[]; visualizeUrl: { app: string; path: string; diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index 620a6dc63a2d0..b5b799fd67942 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -45,6 +45,15 @@ export { applyFilterTrigger, VISUALIZE_FIELD_TRIGGER, visualizeFieldTrigger, + VISUALIZE_GEO_FIELD_TRIGGER, + visualizeGeoFieldTrigger, } from './triggers'; -export { TriggerContextMapping, TriggerId, ActionContextMapping, ActionType } from './types'; +export { + TriggerContextMapping, + TriggerId, + ActionContextMapping, + ActionType, + VisualizeFieldContext, + VisualizeGeoFieldContext, +} from './types'; export { ActionByType } from './actions'; diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index 92a36aaafe6b6..f83cc97c2a8ef 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -24,6 +24,7 @@ import { valueClickTrigger, applyFilterTrigger, visualizeFieldTrigger, + visualizeGeoFieldTrigger, } from './triggers'; export type UiActionsSetup = Pick< @@ -48,6 +49,7 @@ export class UiActionsPlugin implements Plugin { this.service.registerTrigger(valueClickTrigger); this.service.registerTrigger(applyFilterTrigger); this.service.registerTrigger(visualizeFieldTrigger); + this.service.registerTrigger(visualizeGeoFieldTrigger); return this.service; } diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts index 00ca22d9a8431..263e41a378584 100644 --- a/src/plugins/ui_actions/public/triggers/index.ts +++ b/src/plugins/ui_actions/public/triggers/index.ts @@ -24,3 +24,4 @@ export * from './select_range_trigger'; export * from './value_click_trigger'; export * from './apply_filter_trigger'; export * from './visualize_field_trigger'; +export * from './visualize_geo_field_trigger'; diff --git a/src/plugins/ui_actions/public/triggers/visualize_geo_field_trigger.ts b/src/plugins/ui_actions/public/triggers/visualize_geo_field_trigger.ts new file mode 100644 index 0000000000000..5582b3b42660c --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/visualize_geo_field_trigger.ts @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Trigger } from '.'; + +export const VISUALIZE_GEO_FIELD_TRIGGER = 'VISUALIZE_GEO_FIELD_TRIGGER'; +export const visualizeGeoFieldTrigger: Trigger<'VISUALIZE_GEO_FIELD_TRIGGER'> = { + id: VISUALIZE_GEO_FIELD_TRIGGER, + title: 'Visualize Geo field', + description: 'Triggered when user wants to visualize a geo field.', +}; diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index 7c5b5d0a7c36f..4d572ac23c8a2 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -24,6 +24,7 @@ import { VALUE_CLICK_TRIGGER, APPLY_FILTER_TRIGGER, VISUALIZE_FIELD_TRIGGER, + VISUALIZE_GEO_FIELD_TRIGGER, } from './triggers'; import type { RangeSelectContext, ValueClickContext } from '../../embeddable/public'; import type { ApplyGlobalFilterActionContext } from '../../data/public'; @@ -32,6 +33,17 @@ export type TriggerRegistry = Map>; export type ActionRegistry = Map; export type TriggerToActionsRegistry = Map; +export interface VisualizeFieldContext { + fieldName: string; + indexPatternId: string; +} + +export interface VisualizeGeoFieldContext { + fieldName: string; + indexPatternId: string; + tooltipProperties?: string[]; +} + const DEFAULT_TRIGGER = ''; export type TriggerId = keyof TriggerContextMapping; @@ -44,7 +56,8 @@ export interface TriggerContextMapping { [SELECT_RANGE_TRIGGER]: RangeSelectContext; [VALUE_CLICK_TRIGGER]: ValueClickContext; [APPLY_FILTER_TRIGGER]: ApplyGlobalFilterActionContext; - [VISUALIZE_FIELD_TRIGGER]: any; + [VISUALIZE_FIELD_TRIGGER]: VisualizeFieldContext; + [VISUALIZE_GEO_FIELD_TRIGGER]: VisualizeGeoFieldContext; } const DEFAULT_ACTION = ''; diff --git a/src/plugins/visualize/public/actions/visualize_field_action.ts b/src/plugins/visualize/public/actions/visualize_field_action.ts index 121127ddf4ba9..7fc9bfa4dc77a 100644 --- a/src/plugins/visualize/public/actions/visualize_field_action.ts +++ b/src/plugins/visualize/public/actions/visualize_field_action.ts @@ -24,11 +24,6 @@ import { AGGS_TERMS_SIZE_SETTING } from '../../common/constants'; export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD'; -export interface VisualizeFieldContext { - fieldName: string; - indexPatternId: string; -} - export const visualizeFieldAction = createAction({ type: ACTION_VISUALIZE_FIELD, getDisplayName: () => 'Visualize Field', diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index d08f760c85dcf..419f56f93a2ef 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -43,13 +43,13 @@ import { VisualizeServices } from './application/types'; import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; import { SavedObjectsStart } from '../../saved_objects/public'; import { EmbeddableStart } from '../../embeddable/public'; -import { UiActionsStart, VISUALIZE_FIELD_TRIGGER } from '../../ui_actions/public'; -import { setUISettings, setApplication, setIndexPatterns } from './services'; import { - visualizeFieldAction, + UiActionsStart, + VISUALIZE_FIELD_TRIGGER, VisualizeFieldContext, - ACTION_VISUALIZE_FIELD, -} from './actions/visualize_field_action'; +} from '../../ui_actions/public'; +import { setUISettings, setApplication, setIndexPatterns } from './services'; +import { visualizeFieldAction, ACTION_VISUALIZE_FIELD } from './actions/visualize_field_action'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; diff --git a/x-pack/plugins/maps/public/actions/index.ts b/x-pack/plugins/maps/public/actions/index.ts index 5b153e37da5a8..9dbd73b14bd06 100644 --- a/x-pack/plugins/maps/public/actions/index.ts +++ b/x-pack/plugins/maps/public/actions/index.ts @@ -5,6 +5,7 @@ */ export * from './ui_actions'; +export * from './visualize_geo_field_action'; export * from './map_actions'; export * from './map_action_constants'; diff --git a/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts b/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts new file mode 100644 index 0000000000000..687a08a43db1f --- /dev/null +++ b/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import rison from 'rison-node'; +import uuid from 'uuid/v4'; +import { stringify } from 'query-string'; +import { createAction } from '../../../../../src/plugins/ui_actions/public'; +import { getApplication, getIndexPatternService } from '../kibana_services'; + +export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD'; + +export const visualizeGeoFieldAction = createAction({ + type: ACTION_VISUALIZE_GEO_FIELD, + getDisplayName: () => 'Visualize Geo Field', + execute: async (context) => { + const indexPattern = await getIndexPatternService().get(context.indexPatternId); + const field = indexPattern.fields.find((fld) => fld.name === context.fieldName); + const supportsClustering = field?.aggregatable; + // create initial layer descriptor + const hasTooltips = + context.tooltipProperties?.length && context.tooltipProperties[0] !== '_source'; + + const linkUrlParams = { + initialLayers: rison.encode({ + id: uuid(), + indexPattern: context.indexPatternId, + label: indexPattern.title, + visible: true, + type: supportsClustering ? 'BLENDED_VECTOR' : 'VECTOR', + sourceDescriptor: { + id: uuid(), + type: 'ES_SEARCH', + geoField: context.fieldName, + tooltipProperties: hasTooltips ? context.tooltipProperties : [], + indexPatternId: context.indexPatternId, + scalingType: supportsClustering ? 'CLUSTERS' : 'LIMIT', + }, + }), + }; + + getApplication().navigateToApp('maps', { + path: `/map#?${stringify(linkUrlParams)}`, + }); + }, +}); diff --git a/x-pack/plugins/maps/public/kibana_services.d.ts b/x-pack/plugins/maps/public/kibana_services.d.ts index 2e6911e89fa0a..f184304f8a5c4 100644 --- a/x-pack/plugins/maps/public/kibana_services.d.ts +++ b/x-pack/plugins/maps/public/kibana_services.d.ts @@ -6,6 +6,7 @@ import { DataPublicPluginStart } from 'src/plugins/data/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { IndexPatternsService } from 'src/plugins/data/public/index_patterns'; +import { ApplicationStart } from '../../../../src/core/public'; import { MapsConfigType } from '../config'; import { MapsLegacyConfigType } from '../../../../src/plugins/maps_legacy/public'; @@ -31,6 +32,7 @@ export function getUiActions(): any; export function getCore(): any; export function getNavigation(): any; export function getCoreI18n(): any; +export function getApplication(): ApplicationStart; export function getSearchService(): DataPublicPluginStart['search']; export function getKibanaCommonConfig(): MapsLegacyConfigType; export function getMapAppConfig(): MapsConfigType; @@ -72,6 +74,7 @@ export function setUiActions(args: unknown): void; export function setCore(args: unknown): void; export function setNavigation(args: unknown): void; export function setCoreI18n(args: unknown): void; +export function setApplication(args: ApplicationStart): void; export function setSearchService(args: DataPublicPluginStart['search']): void; export function setKibanaCommonConfig(config: MapsLegacyConfigType): void; export function setMapAppConfig(config: MapsConfigType): void; diff --git a/x-pack/plugins/maps/public/kibana_services.js b/x-pack/plugins/maps/public/kibana_services.js index 89d578f27b118..b93e0d1973c4e 100644 --- a/x-pack/plugins/maps/public/kibana_services.js +++ b/x-pack/plugins/maps/public/kibana_services.js @@ -177,3 +177,7 @@ export const setIsGoldPlus = (igp) => { export const getIsGoldPlus = () => { return isGoldPlus; }; + +let coreApplication; +export const setApplication = (application) => (coreApplication = application); +export const getApplication = () => coreApplication; diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 8428a31d8b408..551b134e18637 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -41,12 +41,17 @@ import { setUiActions, setUiSettings, setVisualizations, + setApplication, } from './kibana_services'; import { featureCatalogueEntry } from './feature_catalogue_entry'; // @ts-ignore import { getMapsVisTypeAlias } from './maps_vis_type_alias'; import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public'; +import { + VISUALIZE_GEO_FIELD_TRIGGER, + VisualizeGeoFieldContext, +} from '../../../../src/plugins/ui_actions/public'; import { APP_ICON, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public'; @@ -56,6 +61,7 @@ import { ILicense } from '../../licensing/common/types'; import { lazyLoadMapModules } from './lazy_load_bundle'; import { MapsStartApi } from './api'; import { createSecurityLayerDescriptors, registerLayerWizard, registerSource } from './api'; +import { visualizeGeoFieldAction, ACTION_VISUALIZE_GEO_FIELD } from './actions'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; @@ -67,6 +73,12 @@ export interface MapsPluginSetupDependencies { // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface MapsPluginStartDependencies {} +declare module '../../../../src/plugins/ui_actions/public' { + export interface ActionContextMapping { + [ACTION_VISUALIZE_GEO_FIELD]: VisualizeGeoFieldContext; + } +} + export const bindSetupCoreAndPlugins = ( core: CoreSetup, plugins: any, @@ -96,6 +108,8 @@ export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { }); } + plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction); + setInspector(inspector); setFileUpload(fileUpload); setIndexPatternSelect(data.ui.IndexPatternSelect); @@ -113,6 +127,7 @@ export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { setUiActions(plugins.uiActions); setNavigation(plugins.navigation); setCoreI18n(core.i18n); + setApplication(core.application); }; /** From a590a5e93116b0db5ec6458428d6b714e5e968a4 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 3 Aug 2020 10:20:21 +0300 Subject: [PATCH 04/28] clean up discover code and tile map action implementation --- .../sidebar/discover_field_details.tsx | 4 - .../components/sidebar/lib/get_details.ts | 5 - .../sidebar/lib/visualize_url_utils.ts | 182 ------------------ .../application/components/sidebar/types.ts | 4 - src/plugins/tile_map/kibana.json | 3 +- .../visualize_tilemap_field_actions.ts | 55 ++++++ src/plugins/tile_map/public/plugin.ts | 30 ++- src/plugins/tile_map/public/services.ts | 8 + .../public/vis_types/types_service.ts | 7 + .../actions/visualize_geo_field_action.ts | 2 +- 10 files changed, 100 insertions(+), 200 deletions(-) delete mode 100644 src/plugins/discover/public/application/components/sidebar/lib/visualize_url_utils.ts create mode 100644 src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx index cdbb0023a878f..23d669887fefc 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx @@ -23,7 +23,6 @@ import { DiscoverFieldBucket } from './discover_field_bucket'; import { getWarnings } from './lib/get_warnings'; import { triggerVisualizeActions, isFieldVisualizable } from './lib/get_visualize_trigger'; import { Bucket, FieldDetails } from './types'; -import { getServices } from '../../../kibana_services'; import { IndexPatternField, IndexPattern } from '../../../../../data/public'; interface DiscoverFieldDetailsProps { @@ -91,9 +90,6 @@ export function DiscoverFieldDetails({ <> { - // getServices().core.application.navigateToApp(details.visualizeUrl.app, { - // path: details.visualizeUrl.path, - // }); triggerVisualizeActions(field, indexPattern.id, details.columns); }} className="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall" diff --git a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts index 9ea8cdfbb60fe..194a4ab6ec638 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import { getVisualizeUrl, isFieldVisualizable } from './visualize_url_utils'; import { AppState } from '../../../angular/discover_state'; // @ts-ignore import { fieldCalculator } from './field_calculator'; @@ -32,10 +31,6 @@ export function getDetails( services: DiscoverServices ) { const details = { - visualizeUrl: - services.capabilities.visualize.show && isFieldVisualizable(field, services.visualizations) - ? getVisualizeUrl(field, indexPattern, state, columns, services) - : null, ...fieldCalculator.getFieldValueCounts({ hits, field, diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_url_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_url_utils.ts deleted file mode 100644 index d598f28a0ad12..0000000000000 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_url_utils.ts +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import uuid from 'uuid/v4'; -import rison from 'rison-node'; -import { parse, stringify } from 'query-string'; -import { - IFieldType, - IIndexPattern, - IndexPatternField, - KBN_FIELD_TYPES, -} from '../../../../../../data/public'; -import { AppState } from '../../../angular/discover_state'; -import { DiscoverServices } from '../../../../build_services'; -import { VisualizationsStart, VisTypeAlias } from '../../../../../../visualizations/public'; -import { AGGS_TERMS_SIZE_SETTING } from '../../../../../common'; - -export function isMapsAppRegistered(visualizations: VisualizationsStart) { - return visualizations.getAliases().some(({ name }: VisTypeAlias) => { - return name === 'maps'; - }); -} - -export function isFieldVisualizable(field: IFieldType, visualizations: VisualizationsStart) { - if (field.name === '_id') { - // Else you'd get a 'Fielddata access on the _id field is disallowed' error on ES side. - return false; - } - if ( - (field.type === KBN_FIELD_TYPES.GEO_POINT || field.type === KBN_FIELD_TYPES.GEO_SHAPE) && - isMapsAppRegistered(visualizations) - ) { - return true; - } - return field.visualizable; -} - -export function getMapsAppUrl( - field: IFieldType, - indexPattern: IIndexPattern, - appState: AppState, - columns: string[] -) { - const mapAppParams = new URLSearchParams(); - - // Copy global state - const locationSplit = window.location.hash.split('?'); - if (locationSplit.length > 1) { - const discoverParams = new URLSearchParams(locationSplit[1]); - const globalStateUrlValue = discoverParams.get('_g'); - if (globalStateUrlValue) { - mapAppParams.set('_g', globalStateUrlValue); - } - } - - // Copy filters and query in app state - const mapsAppState: any = { - filters: appState.filters || [], - }; - if (appState.query) { - mapsAppState.query = appState.query; - } - // @ts-ignore - mapAppParams.set('_a', rison.encode(mapsAppState)); - - // create initial layer descriptor - const hasColumns = columns && columns.length && columns[0] !== '_source'; - const supportsClustering = field.aggregatable; - mapAppParams.set( - 'initialLayers', - // @ts-ignore - rison.encode_array([ - { - id: uuid(), - label: indexPattern.title, - sourceDescriptor: { - id: uuid(), - type: 'ES_SEARCH', - geoField: field.name, - tooltipProperties: hasColumns ? columns : [], - indexPatternId: indexPattern.id, - scalingType: supportsClustering ? 'CLUSTERS' : 'LIMIT', - }, - visible: true, - type: supportsClustering ? 'BLENDED_VECTOR' : 'VECTOR', - }, - ]) - ); - - return { - app: 'maps', - path: `/map#?${mapAppParams.toString()}`, - }; -} - -export function getVisualizeUrl( - field: IndexPatternField, - indexPattern: IIndexPattern, - state: AppState, - columns: string[], - services: DiscoverServices -) { - const aggsTermSize = services.uiSettings.get(AGGS_TERMS_SIZE_SETTING); - const urlParams = parse(services.history().location.search) as Record; - - if ( - (field.type === KBN_FIELD_TYPES.GEO_POINT || field.type === KBN_FIELD_TYPES.GEO_SHAPE) && - isMapsAppRegistered(services.visualizations) - ) { - return getMapsAppUrl(field, indexPattern, state, columns); - } - - let agg; - const isGeoPoint = field.type === KBN_FIELD_TYPES.GEO_POINT; - const type = isGeoPoint ? 'tile_map' : 'histogram'; - // If we're visualizing a date field, and our index is time based (and thus has a time filter), - // then run a date histogram - if (field.type === 'date' && indexPattern.timeFieldName === field.name) { - agg = { - type: 'date_histogram', - schema: 'segment', - params: { - field: field.name, - interval: 'auto', - }, - }; - } else if (isGeoPoint) { - agg = { - type: 'geohash_grid', - schema: 'segment', - params: { - field: field.name, - precision: 3, - }, - }; - } else { - agg = { - type: 'terms', - schema: 'segment', - params: { - field: field.name, - size: parseInt(aggsTermSize, 10), - orderBy: '2', - }, - }; - } - const linkUrlParams = { - ...urlParams, - ...{ - indexPattern: state.index!, - type, - _a: rison.encode({ - filters: state.filters || [], - query: state.query, - vis: { - type, - aggs: [{ schema: 'metric', type: 'count', id: '2' }, agg], - }, - } as any), - }, - }; - - return { - app: 'visualize', - path: `#/create?${stringify(linkUrlParams)}`, - }; -} diff --git a/src/plugins/discover/public/application/components/sidebar/types.ts b/src/plugins/discover/public/application/components/sidebar/types.ts index 96a15639b49d4..d80662b65cc7b 100644 --- a/src/plugins/discover/public/application/components/sidebar/types.ts +++ b/src/plugins/discover/public/application/components/sidebar/types.ts @@ -28,10 +28,6 @@ export interface FieldDetails { total: boolean; buckets: Bucket[]; columns: string[]; - visualizeUrl: { - app: string; - path: string; - }; } export interface Bucket { diff --git a/src/plugins/tile_map/kibana.json b/src/plugins/tile_map/kibana.json index 9881a2dd72308..2c184a4a08a5a 100644 --- a/src/plugins/tile_map/kibana.json +++ b/src/plugins/tile_map/kibana.json @@ -10,7 +10,8 @@ "expressions", "mapsLegacy", "kibanaLegacy", - "data" + "data", + "uiActions" ], "requiredBundles": [ "kibanaUtils", diff --git a/src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts b/src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts new file mode 100644 index 0000000000000..f65e08389db1f --- /dev/null +++ b/src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import rison from 'rison-node'; +import { stringify } from 'query-string'; +import { createAction } from '../../../ui_actions/public'; +import { getApplication, getVisualizations } from '../services'; + +export const ACTION_VISUALIZE_TILEMAP_FIELD = 'ACTION_VISUALIZE_TILEMAP_FIELD'; + +export const visualizeTilemapFieldAction = createAction({ + type: ACTION_VISUALIZE_TILEMAP_FIELD, + getDisplayName: () => 'Go to Tile Maps', + isCompatible: async () => !getVisualizations().typeIsHidden('tile_map'), + execute: async (context) => { + const agg = { + type: 'geohash_grid', + schema: 'segment', + params: { + field: context.fieldName, + precision: 3, + }, + }; + + const linkUrlParams = { + indexPattern: context.indexPatternId, + type: 'tile_map', + _a: rison.encode({ + vis: { + type: 'tile_map', + aggs: [{ schema: 'metric', type: 'count', id: '1' }, agg], + }, + } as any), + }; + + getApplication().navigateToApp('visualize', { + path: `#/create?${stringify(linkUrlParams)}`, + }); + }, +}); diff --git a/src/plugins/tile_map/public/plugin.ts b/src/plugins/tile_map/public/plugin.ts index 4582cd2283dc1..0bbc15268dd08 100644 --- a/src/plugins/tile_map/public/plugin.ts +++ b/src/plugins/tile_map/public/plugin.ts @@ -34,9 +34,29 @@ import { createTileMapFn } from './tile_map_fn'; import { createTileMapTypeDefinition } from './tile_map_type'; import { IServiceSettings, MapsLegacyPluginSetup } from '../../maps_legacy/public'; import { DataPublicPluginStart } from '../../data/public'; -import { setFormatService, setQueryService } from './services'; -import { setKibanaLegacy } from './services'; +import { + UiActionsStart, + VISUALIZE_GEO_FIELD_TRIGGER, + VisualizeGeoFieldContext, +} from '../../ui_actions/public'; +import { + setFormatService, + setQueryService, + setKibanaLegacy, + setApplication, + setVisualizations, +} from './services'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; +import { + visualizeTilemapFieldAction, + ACTION_VISUALIZE_TILEMAP_FIELD, +} from './actions/visualize_tilemap_field_actions'; + +declare module '../../ui_actions/public' { + export interface ActionContextMapping { + [ACTION_VISUALIZE_TILEMAP_FIELD]: VisualizeGeoFieldContext; + } +} export interface TileMapConfigType { tilemap: any; @@ -62,6 +82,7 @@ export interface TileMapPluginSetupDependencies { export interface TileMapPluginStartDependencies { data: DataPublicPluginStart; kibanaLegacy: KibanaLegacyStart; + uiActions: UiActionsStart; } export interface TileMapPluginSetup { @@ -83,6 +104,7 @@ export class TileMapPlugin implements Plugin = { getZoomPrecision, getPrecision, @@ -101,10 +123,12 @@ export class TileMapPlugin implements Plugin( 'KibanaLegacy' ); + +export const [getApplication, setApplication] = createGetterSetter('Application'); + +export const [getVisualizations, setVisualizations] = createGetterSetter( + 'Visualizations' +); diff --git a/src/plugins/visualizations/public/vis_types/types_service.ts b/src/plugins/visualizations/public/vis_types/types_service.ts index 14c2a9c50ab0e..777b032e66b1b 100644 --- a/src/plugins/visualizations/public/vis_types/types_service.ts +++ b/src/plugins/visualizations/public/vis_types/types_service.ts @@ -104,6 +104,13 @@ export class TypesService { } }); }, + /** + * returns if a specific type is hidden + * @param {string} typeName - list of type ids to hide + */ + typeIsHidden: (typeName: string) => { + return this.types[typeName].hidden; + }, }; } diff --git a/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts b/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts index 687a08a43db1f..0a307a2fa37eb 100644 --- a/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts +++ b/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts @@ -14,7 +14,7 @@ export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD'; export const visualizeGeoFieldAction = createAction({ type: ACTION_VISUALIZE_GEO_FIELD, - getDisplayName: () => 'Visualize Geo Field', + getDisplayName: () => 'Go to Maps', execute: async (context) => { const indexPattern = await getIndexPatternService().get(context.indexPatternId); const field = indexPattern.fields.find((fld) => fld.name === context.fieldName); From 814d7ccc3ddda6cba716ed0dfd3597da868c1509 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 3 Aug 2020 10:33:52 +0300 Subject: [PATCH 05/28] Add typeIsHidden on mocks --- src/plugins/visualizations/public/mocks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts index e0ec4801b3caf..84a566f812a57 100644 --- a/src/plugins/visualizations/public/mocks.ts +++ b/src/plugins/visualizations/public/mocks.ts @@ -33,6 +33,7 @@ const createSetupContract = (): VisualizationsSetup => ({ createReactVisualization: jest.fn(), registerAlias: jest.fn(), hideTypes: jest.fn(), + typeIsHidden: jest.fn(), }); const createStartContract = (): VisualizationsStart => ({ From baf82b0557d36f5ee6eac9f5525018c362be56c3 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 3 Aug 2020 16:27:26 +0300 Subject: [PATCH 06/28] Retrieve filters and query from url state --- .../sidebar/discover_field_details.tsx | 10 ++++----- ..._trigger.ts => visualize_trigger_utils.ts} | 16 +++----------- src/plugins/discover/public/index.ts | 1 + .../visualize_tilemap_field_actions.ts | 12 ++++++++++- src/plugins/tile_map/public/plugin.ts | 4 ++-- src/plugins/ui_actions/public/index.ts | 1 - src/plugins/ui_actions/public/types.ts | 8 +------ .../public/actions/visualize_field_action.ts | 13 +++++++++++- .../actions/visualize_geo_field_action.ts | 21 ++++++++++++++++--- x-pack/plugins/maps/public/plugin.ts | 4 ++-- 10 files changed, 55 insertions(+), 35 deletions(-) rename src/plugins/discover/public/application/components/sidebar/lib/{get_visualize_trigger.ts => visualize_trigger_utils.ts} (84%) diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx index 23d669887fefc..8c6dfdd97d4e1 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx @@ -21,7 +21,7 @@ import { EuiLink, EuiIconTip, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { DiscoverFieldBucket } from './discover_field_bucket'; import { getWarnings } from './lib/get_warnings'; -import { triggerVisualizeActions, isFieldVisualizable } from './lib/get_visualize_trigger'; +import { triggerVisualizeActions, isFieldVisualizable } from './lib/visualize_trigger_utils'; import { Bucket, FieldDetails } from './types'; import { IndexPatternField, IndexPattern } from '../../../../../data/public'; @@ -41,9 +41,9 @@ export function DiscoverFieldDetails({ const warnings = getWarnings(field); const [showVisualizeLink, setShowVisualizeLink] = useState(false); - isFieldVisualizable(field, indexPattern.id, details.columns).then( - (v) => { - setShowVisualizeLink(v); + isFieldVisualizable(field, indexPattern.id).then( + (flag) => { + setShowVisualizeLink(flag); }, () => { setShowVisualizeLink(false); @@ -90,7 +90,7 @@ export function DiscoverFieldDetails({ <> { - triggerVisualizeActions(field, indexPattern.id, details.columns); + triggerVisualizeActions(field, indexPattern.id); }} className="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall" data-test-subj={`fieldVisualize-${field.name}`} diff --git a/src/plugins/discover/public/application/components/sidebar/lib/get_visualize_trigger.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts similarity index 84% rename from src/plugins/discover/public/application/components/sidebar/lib/get_visualize_trigger.ts rename to src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts index b815d1e98c28c..8cf856284a879 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/get_visualize_trigger.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts @@ -30,36 +30,31 @@ function getTrigger(type: string) { async function getCompatibleActions( fieldName: string, indexPatternId: string, - tooltipProperties: string[], action: typeof VISUALIZE_FIELD_TRIGGER | typeof VISUALIZE_GEO_FIELD_TRIGGER ) { const compatibleActions = await getUiActions().getTriggerCompatibleActions(action, { indexPatternId, fieldName, - tooltipProperties, }); return compatibleActions.length; } export function triggerVisualizeActions( field: IndexPatternField, - indexPatternId: string | undefined, - tooltipProperties: string[] + indexPatternId: string | undefined ) { if (!indexPatternId) return; const trigger = getTrigger(field.type); const triggerOptions = { indexPatternId, fieldName: field.name, - tooltipProperties: trigger === VISUALIZE_GEO_FIELD_TRIGGER ? tooltipProperties : [], }; getUiActions().executeTriggerActions(trigger, triggerOptions); } export async function isFieldVisualizable( field: IndexPatternField, - indexPatternId: string | undefined, - tooltipProperties: string[] + indexPatternId: string | undefined ) { if (!indexPatternId) return; if (field.name === '_id') { @@ -67,11 +62,6 @@ export async function isFieldVisualizable( return false; } const trigger = getTrigger(field.type); - const compatibleActions = await getCompatibleActions( - field.name, - indexPatternId, - tooltipProperties, - trigger - ); + const compatibleActions = await getCompatibleActions(field.name, indexPatternId, trigger); return compatibleActions > 0 && field.visualizable; } diff --git a/src/plugins/discover/public/index.ts b/src/plugins/discover/public/index.ts index 6ac8f674b6153..f555dd256f4c2 100644 --- a/src/plugins/discover/public/index.ts +++ b/src/plugins/discover/public/index.ts @@ -28,3 +28,4 @@ export function plugin(initializerContext: PluginInitializerContext) { export { SavedSearch, SavedSearchLoader, createSavedSearchesLoader } from './saved_searches'; export { ISearchEmbeddable, SEARCH_EMBEDDABLE_TYPE, SearchInput } from './application/embeddable'; export { DISCOVER_APP_URL_GENERATOR, DiscoverUrlGeneratorState } from './url_generator'; +export { AppState as DiscoverAppState } from './application/angular/discover_state'; diff --git a/src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts b/src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts index f65e08389db1f..181ba027b5130 100644 --- a/src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts +++ b/src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts @@ -18,16 +18,24 @@ */ import rison from 'rison-node'; import { stringify } from 'query-string'; +import { i18n } from '@kbn/i18n'; import { createAction } from '../../../ui_actions/public'; +import { DiscoverAppState } from '../../../discover/public'; +import { createKbnUrlStateStorage, IKbnUrlStateStorage } from '../../../kibana_utils/public'; import { getApplication, getVisualizations } from '../services'; export const ACTION_VISUALIZE_TILEMAP_FIELD = 'ACTION_VISUALIZE_TILEMAP_FIELD'; export const visualizeTilemapFieldAction = createAction({ type: ACTION_VISUALIZE_TILEMAP_FIELD, - getDisplayName: () => 'Go to Tile Maps', + getDisplayName: () => + i18n.translate('tileMap.discover.visualizeFieldLabel', { + defaultMessage: 'Visualize on Coordinate Maps', + }), isCompatible: async () => !getVisualizations().typeIsHidden('tile_map'), execute: async (context) => { + const stateStorage: IKbnUrlStateStorage = createKbnUrlStateStorage(); + const appStateFromUrl: DiscoverAppState | null = stateStorage.get('_a'); const agg = { type: 'geohash_grid', schema: 'segment', @@ -41,6 +49,8 @@ export const visualizeTilemapFieldAction = createAction({ type: ACTION_VISUALIZE_FIELD, - getDisplayName: () => 'Visualize Field', + getDisplayName: () => + i18n.translate('visualize.discover.visualizeFieldLabel', { + defaultMessage: 'Visualize on Vis Editor', + }), execute: async (context) => { const indexPattern = await getIndexPatterns().get(context.indexPatternId); const field = indexPattern.fields.find((fld) => fld.name === context.fieldName); const aggsTermSize = getUISettings().get(AGGS_TERMS_SIZE_SETTING); let agg; + const stateStorage: IKbnUrlStateStorage = createKbnUrlStateStorage(); + const appStateFromUrl: DiscoverAppState | null = stateStorage.get('_a'); + // If we're visualizing a date field, and our index is time based (and thus has a time filter), // then run a date histogram if (field?.type === 'date' && indexPattern.timeFieldName === context.fieldName) { @@ -60,6 +69,8 @@ export const visualizeFieldAction = createAction( indexPattern: context.indexPatternId, type: 'histogram', _a: rison.encode({ + filters: appStateFromUrl?.filters || [], + query: appStateFromUrl?.query, vis: { type: 'histogram', aggs: [{ schema: 'metric', type: 'count', id: '1' }, agg], diff --git a/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts b/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts index 0a307a2fa37eb..fd87684d9896c 100644 --- a/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts +++ b/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts @@ -7,23 +7,38 @@ import rison from 'rison-node'; import uuid from 'uuid/v4'; import { stringify } from 'query-string'; +import { i18n } from '@kbn/i18n'; import { createAction } from '../../../../../src/plugins/ui_actions/public'; +import { + createKbnUrlStateStorage, + IKbnUrlStateStorage, +} from '../../../../../src/plugins/kibana_utils/public'; +import { DiscoverAppState } from '../../../../../src/plugins/discover/public'; import { getApplication, getIndexPatternService } from '../kibana_services'; export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD'; export const visualizeGeoFieldAction = createAction({ type: ACTION_VISUALIZE_GEO_FIELD, - getDisplayName: () => 'Go to Maps', + getDisplayName: () => + i18n.translate('xpack.maps.discover.visualizeFieldLabel', { + defaultMessage: 'Visualize on Kibana Maps', + }), execute: async (context) => { const indexPattern = await getIndexPatternService().get(context.indexPatternId); const field = indexPattern.fields.find((fld) => fld.name === context.fieldName); const supportsClustering = field?.aggregatable; + const stateStorage: IKbnUrlStateStorage = createKbnUrlStateStorage(); + const appStateFromUrl: DiscoverAppState | null = stateStorage.get('_a'); // create initial layer descriptor const hasTooltips = - context.tooltipProperties?.length && context.tooltipProperties[0] !== '_source'; + appStateFromUrl?.columns?.length && appStateFromUrl?.columns[0] !== '_source'; const linkUrlParams = { + _a: rison.encode({ + filters: appStateFromUrl?.filters || [], + query: appStateFromUrl?.query, + } as any), initialLayers: rison.encode({ id: uuid(), indexPattern: context.indexPatternId, @@ -34,7 +49,7 @@ export const visualizeGeoFieldAction = createAction Date: Mon, 3 Aug 2020 17:03:50 +0300 Subject: [PATCH 07/28] functional test for oss and tile map --- .../apps/discover/_field_visualize.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/functional/apps/discover/_field_visualize.ts b/test/functional/apps/discover/_field_visualize.ts index b0db6c149e41a..7a92b16bc2971 100644 --- a/test/functional/apps/discover/_field_visualize.ts +++ b/test/functional/apps/discover/_field_visualize.ts @@ -87,6 +87,38 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await inspector.close(); }); + it('should visualize a geo field in coordinate map', async () => { + await PageObjects.discover.findFieldByName('geo.coordinates'); + log.debug('visualize a geo field'); + await PageObjects.discover.clickFieldListItemVisualize('geo.coordinates'); + await PageObjects.header.waitUntilLoadingHasFinished(); + const expectedTableData = [ + ['-', 'dn', '1,429', '{ "lat": 36.3805938706661, "lon": -84.78903367466954 }'], + ['-', 'dp', '1,418', '{ "lat": 41.64736261928247, "lon": -84.89820087802752 }'], + ['-', '9y', '1,215', '{ "lat": 36.45605538343942, "lon": -95.0664490789727 }'], + ['-', '9z', '1,099', '{ "lat": 42.185341429717, "lon": -95.16736012207238 }'], + ['-', 'dr', '1,076', '{ "lat": 42.023513913358, "lon": -73.9809102583313 }'], + ['-', 'dj', '982', '{ "lat": 31.6727389150504, "lon": -84.50814768605602 }'], + ['-', '9v', '938', '{ "lat": 31.380770751890708, "lon": -95.27050333687349 }'], + ['-', '9q', '722', '{ "lat": 36.51360971975679, "lon": -119.18302192208242 }'], + ['-', '9w', '475', '{ "lat": 36.392644499761886, "lon": -106.91101965061144 }'], + ['-', 'cb', '457', '{ "lat": 46.70940757519209, "lon": -95.81077479910212 }'], + ['-', 'c2', '453', '{ "lat": 47.14486789763196, "lon": -119.50036960974681 }'], + ['-', '9x', '420', '{ "lat": 41.80676538663517, "lon": -106.4800124125156 }'], + ['-', 'dq', '399', '{ "lat": 37.1589753382202, "lon": -77.03116585150417 }'], + ['-', '9r', '396', '{ "lat": 41.97058037664233, "lon": -119.63551023403521 }'], + ['-', '9t', '274', '{ "lat": 32.61719440381947, "lon": -106.79003051444752 }'], + ['-', 'c8', '271', '{ "lat": 47.13446403516807, "lon": -106.58752490872881 }'], + ['-', 'dh', '214', '{ "lat": 26.89657606600096, "lon": -81.23893259637163 }'], + ['-', 'b6', '207', '{ "lat": 60.10175546125063, "lon": -161.7005743794953 }'], + ['-', 'bd', '206', '{ "lat": 59.65593476375131, "lon": -152.93652406179356 }'], + ['-', 'b7', '167', '{ "lat": 64.35817028535251, "lon": -162.25999960090274 }'], + ]; + await inspector.open(); + await inspector.expectTableData(expectedTableData); + await inspector.close(); + }); + it('should preserve app filters in visualize', async () => { await filterBar.addFilter('bytes', 'is between', '3500', '4000'); await PageObjects.discover.findFieldByName('geo.src'); From 8c8b6b452c1ef848296bc54afb8ed6e3e4920e95 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 3 Aug 2020 19:19:39 +0300 Subject: [PATCH 08/28] include geoshape --- .../components/sidebar/lib/visualize_trigger_utils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts index 8cf856284a879..741858f426ae9 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts @@ -24,7 +24,9 @@ import { getUiActions } from '../../../../kibana_services'; import { IndexPatternField, KBN_FIELD_TYPES } from '../../../../../../data/public'; function getTrigger(type: string) { - return type === KBN_FIELD_TYPES.GEO_POINT ? VISUALIZE_GEO_FIELD_TRIGGER : VISUALIZE_FIELD_TRIGGER; + return type === KBN_FIELD_TYPES.GEO_POINT || KBN_FIELD_TYPES.GEO_SHAPE + ? VISUALIZE_GEO_FIELD_TRIGGER + : VISUALIZE_FIELD_TRIGGER; } async function getCompatibleActions( From b7ec202ffa709591305386bbdf9f3bbe3718e848 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 4 Aug 2020 11:30:06 +0300 Subject: [PATCH 09/28] fix functional tests --- .../components/sidebar/lib/visualize_trigger_utils.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts index 741858f426ae9..c76b76c9fc1ce 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts @@ -20,11 +20,11 @@ import { VISUALIZE_FIELD_TRIGGER, VISUALIZE_GEO_FIELD_TRIGGER, } from '../../../../../../ui_actions/public'; -import { getUiActions } from '../../../../kibana_services'; +import { getUiActions, getServices } from '../../../../kibana_services'; import { IndexPatternField, KBN_FIELD_TYPES } from '../../../../../../data/public'; function getTrigger(type: string) { - return type === KBN_FIELD_TYPES.GEO_POINT || KBN_FIELD_TYPES.GEO_SHAPE + return type === KBN_FIELD_TYPES.GEO_POINT || type === KBN_FIELD_TYPES.GEO_SHAPE ? VISUALIZE_GEO_FIELD_TRIGGER : VISUALIZE_FIELD_TRIGGER; } @@ -64,6 +64,7 @@ export async function isFieldVisualizable( return false; } const trigger = getTrigger(field.type); + const services = getServices(); const compatibleActions = await getCompatibleActions(field.name, indexPatternId, trigger); - return compatibleActions > 0 && field.visualizable; + return compatibleActions > 0 && field.visualizable && services.capabilities.visualize.show; } From aae1b6cf55b39b06df6d03d690c02d0b855e87a8 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 4 Aug 2020 11:46:12 +0300 Subject: [PATCH 10/28] fix types --- .../components/sidebar/lib/visualize_trigger_utils.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts index c76b76c9fc1ce..0dbaa11dc2109 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts @@ -22,6 +22,7 @@ import { } from '../../../../../../ui_actions/public'; import { getUiActions, getServices } from '../../../../kibana_services'; import { IndexPatternField, KBN_FIELD_TYPES } from '../../../../../../data/public'; +import { DiscoverServices } from '../../../../build_services'; function getTrigger(type: string) { return type === KBN_FIELD_TYPES.GEO_POINT || type === KBN_FIELD_TYPES.GEO_SHAPE @@ -64,7 +65,7 @@ export async function isFieldVisualizable( return false; } const trigger = getTrigger(field.type); - const services = getServices(); + const services: DiscoverServices = getServices(); const compatibleActions = await getCompatibleActions(field.name, indexPatternId, trigger); - return compatibleActions > 0 && field.visualizable && services.capabilities.visualize.show; + return compatibleActions > 0 && field.visualizable && !!services.capabilities.visualize.show; } From 8c56cf7d57f1fb33fbd13e97930006269a82039d Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 4 Aug 2020 12:04:15 +0300 Subject: [PATCH 11/28] remove unecessary dependencies --- .../components/sidebar/discover_sidebar.tsx | 8 +++----- .../components/sidebar/lib/get_details.ts | 15 +++------------ 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx index e8ed8b80da3bb..9c98f581b9c7e 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx @@ -129,11 +129,9 @@ export function DiscoverSidebar({ [fieldFilterState] ); - const getDetailsByField = useCallback( - (ipField: IndexPatternField) => - getDetails(ipField, selectedIndexPattern, state, columns, hits, services), - [selectedIndexPattern, state, columns, hits, services] - ); + const getDetailsByField = useCallback((ipField: IndexPatternField) => getDetails(ipField, hits), [ + hits, + ]); const popularLimit = services.uiSettings.get(FIELDS_LIMIT_SETTING); const useShortDots = services.uiSettings.get(UI_SETTINGS.SHORT_DOTS_ENABLE); diff --git a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts index 194a4ab6ec638..1381ea3221ffc 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts @@ -16,20 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -import { AppState } from '../../../angular/discover_state'; + // @ts-ignore import { fieldCalculator } from './field_calculator'; -import { IndexPatternField, IndexPattern } from '../../../../../../data/public'; -import { DiscoverServices } from '../../../../build_services'; +import { IndexPatternField } from '../../../../../../data/public'; -export function getDetails( - field: IndexPatternField, - indexPattern: IndexPattern, - state: AppState, - columns: string[], - hits: Array>, - services: DiscoverServices -) { +export function getDetails(field: IndexPatternField, hits: Array>) { const details = { ...fieldCalculator.getFieldValueCounts({ hits, @@ -37,7 +29,6 @@ export function getDetails( count: 5, grouped: false, }), - columns, }; if (details.buckets) { for (const bucket of details.buckets) { From ffc0008b210fd01c65b945d819d0822f9c08b68f Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 4 Aug 2020 13:51:27 +0300 Subject: [PATCH 12/28] minor fixes --- .../components/sidebar/lib/visualize_trigger_utils.ts | 6 ++++-- .../discover/public/application/components/sidebar/types.ts | 1 - src/plugins/visualize/public/plugin.ts | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts index 0dbaa11dc2109..3f79ed01f68e8 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts @@ -39,7 +39,7 @@ async function getCompatibleActions( indexPatternId, fieldName, }); - return compatibleActions.length; + return compatibleActions; } export function triggerVisualizeActions( @@ -67,5 +67,7 @@ export async function isFieldVisualizable( const trigger = getTrigger(field.type); const services: DiscoverServices = getServices(); const compatibleActions = await getCompatibleActions(field.name, indexPatternId, trigger); - return compatibleActions > 0 && field.visualizable && !!services.capabilities.visualize.show; + return ( + compatibleActions.length > 0 && field.visualizable && !!services.capabilities.visualize.show + ); } diff --git a/src/plugins/discover/public/application/components/sidebar/types.ts b/src/plugins/discover/public/application/components/sidebar/types.ts index d80662b65cc7b..5025b5c33cba5 100644 --- a/src/plugins/discover/public/application/components/sidebar/types.ts +++ b/src/plugins/discover/public/application/components/sidebar/types.ts @@ -27,7 +27,6 @@ export interface FieldDetails { exists: number; total: boolean; buckets: Bucket[]; - columns: string[]; } export interface Bucket { diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 419f56f93a2ef..17c35b7ee13fc 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -219,7 +219,6 @@ export class VisualizePlugin public start(core: CoreStart, plugins: VisualizePluginStartDependencies) { setApplication(core.application); setIndexPatterns(plugins.data.indexPatterns); - // const visualizeFieldAction = createVisualizeFieldAction(core.application); plugins.uiActions.addTriggerAction(VISUALIZE_FIELD_TRIGGER, visualizeFieldAction); } From f523d7c04bdc81dd63b6a479fe3fdb90e7e87112 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 5 Aug 2020 09:32:19 +0300 Subject: [PATCH 13/28] Remove tilemaps actios as it is going tobe deprecated --- src/plugins/tile_map/kibana.json | 3 +- .../visualize_tilemap_field_actions.ts | 65 ------------------- src/plugins/tile_map/public/plugin.ts | 29 +-------- src/plugins/tile_map/public/services.ts | 8 --- src/plugins/visualizations/public/mocks.ts | 1 - .../public/vis_types/types_service.ts | 7 -- .../apps/discover/_field_visualize.ts | 30 +-------- 7 files changed, 5 insertions(+), 138 deletions(-) delete mode 100644 src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts diff --git a/src/plugins/tile_map/kibana.json b/src/plugins/tile_map/kibana.json index 2c184a4a08a5a..9881a2dd72308 100644 --- a/src/plugins/tile_map/kibana.json +++ b/src/plugins/tile_map/kibana.json @@ -10,8 +10,7 @@ "expressions", "mapsLegacy", "kibanaLegacy", - "data", - "uiActions" + "data" ], "requiredBundles": [ "kibanaUtils", diff --git a/src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts b/src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts deleted file mode 100644 index 181ba027b5130..0000000000000 --- a/src/plugins/tile_map/public/actions/visualize_tilemap_field_actions.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import rison from 'rison-node'; -import { stringify } from 'query-string'; -import { i18n } from '@kbn/i18n'; -import { createAction } from '../../../ui_actions/public'; -import { DiscoverAppState } from '../../../discover/public'; -import { createKbnUrlStateStorage, IKbnUrlStateStorage } from '../../../kibana_utils/public'; -import { getApplication, getVisualizations } from '../services'; - -export const ACTION_VISUALIZE_TILEMAP_FIELD = 'ACTION_VISUALIZE_TILEMAP_FIELD'; - -export const visualizeTilemapFieldAction = createAction({ - type: ACTION_VISUALIZE_TILEMAP_FIELD, - getDisplayName: () => - i18n.translate('tileMap.discover.visualizeFieldLabel', { - defaultMessage: 'Visualize on Coordinate Maps', - }), - isCompatible: async () => !getVisualizations().typeIsHidden('tile_map'), - execute: async (context) => { - const stateStorage: IKbnUrlStateStorage = createKbnUrlStateStorage(); - const appStateFromUrl: DiscoverAppState | null = stateStorage.get('_a'); - const agg = { - type: 'geohash_grid', - schema: 'segment', - params: { - field: context.fieldName, - precision: 3, - }, - }; - - const linkUrlParams = { - indexPattern: context.indexPatternId, - type: 'tile_map', - _a: rison.encode({ - filters: appStateFromUrl?.filters || [], - query: appStateFromUrl?.query, - vis: { - type: 'tile_map', - aggs: [{ schema: 'metric', type: 'count', id: '1' }, agg], - }, - } as any), - }; - - getApplication().navigateToApp('visualize', { - path: `#/create?${stringify(linkUrlParams)}`, - }); - }, -}); diff --git a/src/plugins/tile_map/public/plugin.ts b/src/plugins/tile_map/public/plugin.ts index 382d7e9e9b945..9a164f8a303f8 100644 --- a/src/plugins/tile_map/public/plugin.ts +++ b/src/plugins/tile_map/public/plugin.ts @@ -34,29 +34,8 @@ import { createTileMapFn } from './tile_map_fn'; import { createTileMapTypeDefinition } from './tile_map_type'; import { IServiceSettings, MapsLegacyPluginSetup } from '../../maps_legacy/public'; import { DataPublicPluginStart } from '../../data/public'; -import { - UiActionsStart, - VISUALIZE_GEO_FIELD_TRIGGER, - VisualizeFieldContext, -} from '../../ui_actions/public'; -import { - setFormatService, - setQueryService, - setKibanaLegacy, - setApplication, - setVisualizations, -} from './services'; +import { setFormatService, setQueryService, setKibanaLegacy } from './services'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; -import { - visualizeTilemapFieldAction, - ACTION_VISUALIZE_TILEMAP_FIELD, -} from './actions/visualize_tilemap_field_actions'; - -declare module '../../ui_actions/public' { - export interface ActionContextMapping { - [ACTION_VISUALIZE_TILEMAP_FIELD]: VisualizeFieldContext; - } -} export interface TileMapConfigType { tilemap: any; @@ -82,7 +61,6 @@ export interface TileMapPluginSetupDependencies { export interface TileMapPluginStartDependencies { data: DataPublicPluginStart; kibanaLegacy: KibanaLegacyStart; - uiActions: UiActionsStart; } export interface TileMapPluginSetup { @@ -104,7 +82,6 @@ export class TileMapPlugin implements Plugin = { getZoomPrecision, getPrecision, @@ -123,12 +100,10 @@ export class TileMapPlugin implements Plugin( 'KibanaLegacy' ); - -export const [getApplication, setApplication] = createGetterSetter('Application'); - -export const [getVisualizations, setVisualizations] = createGetterSetter( - 'Visualizations' -); diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts index 84a566f812a57..e0ec4801b3caf 100644 --- a/src/plugins/visualizations/public/mocks.ts +++ b/src/plugins/visualizations/public/mocks.ts @@ -33,7 +33,6 @@ const createSetupContract = (): VisualizationsSetup => ({ createReactVisualization: jest.fn(), registerAlias: jest.fn(), hideTypes: jest.fn(), - typeIsHidden: jest.fn(), }); const createStartContract = (): VisualizationsStart => ({ diff --git a/src/plugins/visualizations/public/vis_types/types_service.ts b/src/plugins/visualizations/public/vis_types/types_service.ts index 777b032e66b1b..14c2a9c50ab0e 100644 --- a/src/plugins/visualizations/public/vis_types/types_service.ts +++ b/src/plugins/visualizations/public/vis_types/types_service.ts @@ -104,13 +104,6 @@ export class TypesService { } }); }, - /** - * returns if a specific type is hidden - * @param {string} typeName - list of type ids to hide - */ - typeIsHidden: (typeName: string) => { - return this.types[typeName].hidden; - }, }; } diff --git a/test/functional/apps/discover/_field_visualize.ts b/test/functional/apps/discover/_field_visualize.ts index 7a92b16bc2971..c95211e98cdba 100644 --- a/test/functional/apps/discover/_field_visualize.ts +++ b/test/functional/apps/discover/_field_visualize.ts @@ -87,36 +87,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await inspector.close(); }); - it('should visualize a geo field in coordinate map', async () => { + it('should not show the "Visualize" button for geo field', async () => { await PageObjects.discover.findFieldByName('geo.coordinates'); log.debug('visualize a geo field'); - await PageObjects.discover.clickFieldListItemVisualize('geo.coordinates'); - await PageObjects.header.waitUntilLoadingHasFinished(); - const expectedTableData = [ - ['-', 'dn', '1,429', '{ "lat": 36.3805938706661, "lon": -84.78903367466954 }'], - ['-', 'dp', '1,418', '{ "lat": 41.64736261928247, "lon": -84.89820087802752 }'], - ['-', '9y', '1,215', '{ "lat": 36.45605538343942, "lon": -95.0664490789727 }'], - ['-', '9z', '1,099', '{ "lat": 42.185341429717, "lon": -95.16736012207238 }'], - ['-', 'dr', '1,076', '{ "lat": 42.023513913358, "lon": -73.9809102583313 }'], - ['-', 'dj', '982', '{ "lat": 31.6727389150504, "lon": -84.50814768605602 }'], - ['-', '9v', '938', '{ "lat": 31.380770751890708, "lon": -95.27050333687349 }'], - ['-', '9q', '722', '{ "lat": 36.51360971975679, "lon": -119.18302192208242 }'], - ['-', '9w', '475', '{ "lat": 36.392644499761886, "lon": -106.91101965061144 }'], - ['-', 'cb', '457', '{ "lat": 46.70940757519209, "lon": -95.81077479910212 }'], - ['-', 'c2', '453', '{ "lat": 47.14486789763196, "lon": -119.50036960974681 }'], - ['-', '9x', '420', '{ "lat": 41.80676538663517, "lon": -106.4800124125156 }'], - ['-', 'dq', '399', '{ "lat": 37.1589753382202, "lon": -77.03116585150417 }'], - ['-', '9r', '396', '{ "lat": 41.97058037664233, "lon": -119.63551023403521 }'], - ['-', '9t', '274', '{ "lat": 32.61719440381947, "lon": -106.79003051444752 }'], - ['-', 'c8', '271', '{ "lat": 47.13446403516807, "lon": -106.58752490872881 }'], - ['-', 'dh', '214', '{ "lat": 26.89657606600096, "lon": -81.23893259637163 }'], - ['-', 'b6', '207', '{ "lat": 60.10175546125063, "lon": -161.7005743794953 }'], - ['-', 'bd', '206', '{ "lat": 59.65593476375131, "lon": -152.93652406179356 }'], - ['-', 'b7', '167', '{ "lat": 64.35817028535251, "lon": -162.25999960090274 }'], - ]; - await inspector.open(); - await inspector.expectTableData(expectedTableData); - await inspector.close(); + await PageObjects.discover.expectMissingFieldListItemVisualize('geo.coordinates'); }); it('should preserve app filters in visualize', async () => { From 4c1038770924ae707387473a622205c7c67285ac Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 5 Aug 2020 12:00:58 +0300 Subject: [PATCH 14/28] Add useEffect on discover details and move the map action to a separate folder --- .../sidebar/discover_field_details.tsx | 20 ++++++++++--------- x-pack/plugins/maps/public/actions/index.ts | 1 - x-pack/plugins/maps/public/plugin.ts | 5 ++++- .../visualize_geo_field_action.ts | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) rename x-pack/plugins/maps/public/{actions => trigger_actions}/visualize_geo_field_action.ts (97%) diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx index 8c6dfdd97d4e1..876b38f63fc5a 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { EuiLink, EuiIconTip, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { DiscoverFieldBucket } from './discover_field_bucket'; @@ -41,14 +41,16 @@ export function DiscoverFieldDetails({ const warnings = getWarnings(field); const [showVisualizeLink, setShowVisualizeLink] = useState(false); - isFieldVisualizable(field, indexPattern.id).then( - (flag) => { - setShowVisualizeLink(flag); - }, - () => { - setShowVisualizeLink(false); - } - ); + useEffect(() => { + isFieldVisualizable(field, indexPattern.id).then( + (flag) => { + setShowVisualizeLink(flag); + }, + () => { + setShowVisualizeLink(false); + } + ); + }, [field, indexPattern.id]); return (
diff --git a/x-pack/plugins/maps/public/actions/index.ts b/x-pack/plugins/maps/public/actions/index.ts index 9dbd73b14bd06..5b153e37da5a8 100644 --- a/x-pack/plugins/maps/public/actions/index.ts +++ b/x-pack/plugins/maps/public/actions/index.ts @@ -5,7 +5,6 @@ */ export * from './ui_actions'; -export * from './visualize_geo_field_action'; export * from './map_actions'; export * from './map_action_constants'; diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 4c67fa10bfdab..8fc415e261f88 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -61,7 +61,10 @@ import { ILicense } from '../../licensing/common/types'; import { lazyLoadMapModules } from './lazy_load_bundle'; import { MapsStartApi } from './api'; import { createSecurityLayerDescriptors, registerLayerWizard, registerSource } from './api'; -import { visualizeGeoFieldAction, ACTION_VISUALIZE_GEO_FIELD } from './actions'; +import { + visualizeGeoFieldAction, + ACTION_VISUALIZE_GEO_FIELD, +} from './trigger_actions/visualize_geo_field_action'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; diff --git a/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts b/x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts similarity index 97% rename from x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts rename to x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts index fd87684d9896c..652401ad6bd61 100644 --- a/x-pack/plugins/maps/public/actions/visualize_geo_field_action.ts +++ b/x-pack/plugins/maps/public/trigger_actions/visualize_geo_field_action.ts @@ -22,7 +22,7 @@ export const visualizeGeoFieldAction = createAction i18n.translate('xpack.maps.discover.visualizeFieldLabel', { - defaultMessage: 'Visualize on Kibana Maps', + defaultMessage: 'Visualize on a map', }), execute: async (context) => { const indexPattern = await getIndexPatternService().get(context.indexPatternId); From f4f2c0dd83d4dd1a6c205c4c8d21dbc118957075 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 5 Aug 2020 12:43:33 +0300 Subject: [PATCH 15/28] Retrieve map tooltips info from context --- .../sidebar/discover_field_details.tsx | 6 +++--- .../components/sidebar/discover_sidebar.tsx | 7 ++++--- .../components/sidebar/lib/get_details.ts | 7 ++++++- .../sidebar/lib/visualize_trigger_utils.ts | 16 +++++++++++++--- .../application/components/sidebar/types.ts | 1 + src/plugins/ui_actions/public/types.ts | 1 + .../visualize_geo_field_action.ts | 4 ++-- 7 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx index 876b38f63fc5a..9412312a24671 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx @@ -42,7 +42,7 @@ export function DiscoverFieldDetails({ const [showVisualizeLink, setShowVisualizeLink] = useState(false); useEffect(() => { - isFieldVisualizable(field, indexPattern.id).then( + isFieldVisualizable(field, indexPattern.id, details.columns).then( (flag) => { setShowVisualizeLink(flag); }, @@ -50,7 +50,7 @@ export function DiscoverFieldDetails({ setShowVisualizeLink(false); } ); - }, [field, indexPattern.id]); + }, [field, indexPattern.id, details.columns]); return (
@@ -92,7 +92,7 @@ export function DiscoverFieldDetails({ <> { - triggerVisualizeActions(field, indexPattern.id); + triggerVisualizeActions(field, indexPattern.id, details.columns); }} className="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall" data-test-subj={`fieldVisualize-${field.name}`} diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx index 9c98f581b9c7e..abf7974ff1844 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx @@ -129,9 +129,10 @@ export function DiscoverSidebar({ [fieldFilterState] ); - const getDetailsByField = useCallback((ipField: IndexPatternField) => getDetails(ipField, hits), [ - hits, - ]); + const getDetailsByField = useCallback( + (ipField: IndexPatternField) => getDetails(ipField, hits, columns), + [hits, columns] + ); const popularLimit = services.uiSettings.get(FIELDS_LIMIT_SETTING); const useShortDots = services.uiSettings.get(UI_SETTINGS.SHORT_DOTS_ENABLE); diff --git a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts index 1381ea3221ffc..41d3393672474 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/get_details.ts @@ -21,7 +21,11 @@ import { fieldCalculator } from './field_calculator'; import { IndexPatternField } from '../../../../../../data/public'; -export function getDetails(field: IndexPatternField, hits: Array>) { +export function getDetails( + field: IndexPatternField, + hits: Array>, + columns: string[] +) { const details = { ...fieldCalculator.getFieldValueCounts({ hits, @@ -29,6 +33,7 @@ export function getDetails(field: IndexPatternField, hits: Array 0 && field.visualizable && !!services.capabilities.visualize.show ); diff --git a/src/plugins/discover/public/application/components/sidebar/types.ts b/src/plugins/discover/public/application/components/sidebar/types.ts index 5025b5c33cba5..d80662b65cc7b 100644 --- a/src/plugins/discover/public/application/components/sidebar/types.ts +++ b/src/plugins/discover/public/application/components/sidebar/types.ts @@ -27,6 +27,7 @@ export interface FieldDetails { exists: number; total: boolean; buckets: Bucket[]; + columns: string[]; } export interface Bucket { diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index 8f669db6d5fa8..31611a44530b4 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -36,6 +36,7 @@ export type TriggerToActionsRegistry = Map; export interface VisualizeFieldContext { fieldName: string; indexPatternId: string; + contextualFields?: string[]; } const DEFAULT_TRIGGER = ''; 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 652401ad6bd61..f4abfa5c6904b 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 @@ -32,7 +32,7 @@ export const visualizeGeoFieldAction = createAction Date: Wed, 5 Aug 2020 13:31:56 +0300 Subject: [PATCH 16/28] Retrieve query and filters from QueryService --- src/plugins/discover/public/index.ts | 1 - .../public/actions/visualize_field_action.ts | 11 +++-------- src/plugins/visualize/public/plugin.ts | 3 ++- src/plugins/visualize/public/services.ts | 6 +++++- x-pack/plugins/maps/public/kibana_services.d.ts | 2 ++ x-pack/plugins/maps/public/kibana_services.js | 4 ++++ x-pack/plugins/maps/public/plugin.ts | 2 ++ .../trigger_actions/visualize_geo_field_action.ts | 13 +++---------- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/plugins/discover/public/index.ts b/src/plugins/discover/public/index.ts index f555dd256f4c2..6ac8f674b6153 100644 --- a/src/plugins/discover/public/index.ts +++ b/src/plugins/discover/public/index.ts @@ -28,4 +28,3 @@ export function plugin(initializerContext: PluginInitializerContext) { export { SavedSearch, SavedSearchLoader, createSavedSearchesLoader } from './saved_searches'; export { ISearchEmbeddable, SEARCH_EMBEDDABLE_TYPE, SearchInput } from './application/embeddable'; export { DISCOVER_APP_URL_GENERATOR, DiscoverUrlGeneratorState } from './url_generator'; -export { AppState as DiscoverAppState } from './application/angular/discover_state'; diff --git a/src/plugins/visualize/public/actions/visualize_field_action.ts b/src/plugins/visualize/public/actions/visualize_field_action.ts index 2c718cdd00a98..367c167150c75 100644 --- a/src/plugins/visualize/public/actions/visualize_field_action.ts +++ b/src/plugins/visualize/public/actions/visualize_field_action.ts @@ -20,9 +20,7 @@ import rison from 'rison-node'; import { stringify } from 'query-string'; import { i18n } from '@kbn/i18n'; import { createAction } from '../../../ui_actions/public'; -import { createKbnUrlStateStorage, IKbnUrlStateStorage } from '../../../kibana_utils/public'; -import { DiscoverAppState } from '../../../discover/public'; -import { getApplication, getUISettings, getIndexPatterns } from '../services'; +import { getApplication, getUISettings, getIndexPatterns, getQueryService } from '../services'; import { AGGS_TERMS_SIZE_SETTING } from '../../common/constants'; export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD'; @@ -39,9 +37,6 @@ export const visualizeFieldAction = createAction( const aggsTermSize = getUISettings().get(AGGS_TERMS_SIZE_SETTING); let agg; - const stateStorage: IKbnUrlStateStorage = createKbnUrlStateStorage(); - const appStateFromUrl: DiscoverAppState | null = stateStorage.get('_a'); - // If we're visualizing a date field, and our index is time based (and thus has a time filter), // then run a date histogram if (field?.type === 'date' && indexPattern.timeFieldName === context.fieldName) { @@ -69,8 +64,8 @@ export const visualizeFieldAction = createAction( indexPattern: context.indexPatternId, type: 'histogram', _a: rison.encode({ - filters: appStateFromUrl?.filters || [], - query: appStateFromUrl?.query, + filters: getQueryService().filterManager.getFilters() || [], + query: getQueryService().queryString.getQuery(), vis: { type: 'histogram', aggs: [{ schema: 'metric', type: 'count', id: '1' }, agg], diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 17c35b7ee13fc..c6169b073d231 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -48,7 +48,7 @@ import { VISUALIZE_FIELD_TRIGGER, VisualizeFieldContext, } from '../../ui_actions/public'; -import { setUISettings, setApplication, setIndexPatterns } from './services'; +import { setUISettings, setApplication, setIndexPatterns, setQueryService } from './services'; import { visualizeFieldAction, ACTION_VISUALIZE_FIELD } from './actions/visualize_field_action'; export interface VisualizePluginStartDependencies { @@ -219,6 +219,7 @@ export class VisualizePlugin public start(core: CoreStart, plugins: VisualizePluginStartDependencies) { setApplication(core.application); setIndexPatterns(plugins.data.indexPatterns); + setQueryService(plugins.data.query); plugins.uiActions.addTriggerAction(VISUALIZE_FIELD_TRIGGER, visualizeFieldAction); } diff --git a/src/plugins/visualize/public/services.ts b/src/plugins/visualize/public/services.ts index e51b4be7fb693..ed1baacce7ef3 100644 --- a/src/plugins/visualize/public/services.ts +++ b/src/plugins/visualize/public/services.ts @@ -19,7 +19,7 @@ import { ApplicationStart, IUiSettingsClient } from '../../../core/public'; import { createGetterSetter } from '../../../plugins/kibana_utils/public'; -import { IndexPatternsContract } from '../../../plugins/data/public'; +import { IndexPatternsContract, DataPublicPluginStart } from '../../../plugins/data/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -28,3 +28,7 @@ export const [getApplication, setApplication] = createGetterSetter( 'IndexPatterns' ); + +export const [getQueryService, setQueryService] = createGetterSetter< + DataPublicPluginStart['query'] +>('Query'); diff --git a/x-pack/plugins/maps/public/kibana_services.d.ts b/x-pack/plugins/maps/public/kibana_services.d.ts index f184304f8a5c4..4ae014318cd1f 100644 --- a/x-pack/plugins/maps/public/kibana_services.d.ts +++ b/x-pack/plugins/maps/public/kibana_services.d.ts @@ -34,6 +34,7 @@ export function getNavigation(): any; export function getCoreI18n(): any; export function getApplication(): ApplicationStart; export function getSearchService(): DataPublicPluginStart['search']; +export function getQueryService(): DataPublicPluginStart['query']; export function getKibanaCommonConfig(): MapsLegacyConfigType; export function getMapAppConfig(): MapsConfigType; export function getIsEmsEnabled(): any; @@ -76,6 +77,7 @@ export function setNavigation(args: unknown): void; export function setCoreI18n(args: unknown): void; export function setApplication(args: ApplicationStart): void; export function setSearchService(args: DataPublicPluginStart['search']): void; +export function setQueryService(args: DataPublicPluginStart['query']): void; export function setKibanaCommonConfig(config: MapsLegacyConfigType): void; export function setMapAppConfig(config: MapsConfigType): void; export function setKibanaVersion(version: string): void; diff --git a/x-pack/plugins/maps/public/kibana_services.js b/x-pack/plugins/maps/public/kibana_services.js index b93e0d1973c4e..f45a3769fdf8f 100644 --- a/x-pack/plugins/maps/public/kibana_services.js +++ b/x-pack/plugins/maps/public/kibana_services.js @@ -139,6 +139,10 @@ let dataSearchService; export const setSearchService = (searchService) => (dataSearchService = searchService); export const getSearchService = () => dataSearchService; +let dataQueryService; +export const setQueryService = (queryService) => (dataQueryService = queryService); +export const getQueryService = () => dataQueryService; + let kibanaVersion; export const setKibanaVersion = (version) => (kibanaVersion = version); export const getKibanaVersion = () => kibanaVersion; diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 8fc415e261f88..0b3c5076401c1 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -42,6 +42,7 @@ import { setUiSettings, setVisualizations, setApplication, + setQueryService, } from './kibana_services'; import { featureCatalogueEntry } from './feature_catalogue_entry'; // @ts-ignore @@ -118,6 +119,7 @@ export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { setIndexPatternSelect(data.ui.IndexPatternSelect); setTimeFilter(data.query.timefilter.timefilter); setSearchService(data.search); + setQueryService(data.query); setIndexPatternService(data.indexPatterns); setAutocompleteService(data.autocomplete); setCore(core); 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 f4abfa5c6904b..ad9e0ac6a81ab 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 @@ -9,12 +9,7 @@ import uuid from 'uuid/v4'; import { stringify } from 'query-string'; import { i18n } from '@kbn/i18n'; import { createAction } from '../../../../../src/plugins/ui_actions/public'; -import { - createKbnUrlStateStorage, - IKbnUrlStateStorage, -} from '../../../../../src/plugins/kibana_utils/public'; -import { DiscoverAppState } from '../../../../../src/plugins/discover/public'; -import { getApplication, getIndexPatternService } from '../kibana_services'; +import { getApplication, getIndexPatternService, getQueryService } from '../kibana_services'; export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD'; @@ -28,16 +23,14 @@ export const visualizeGeoFieldAction = createAction fld.name === context.fieldName); const supportsClustering = field?.aggregatable; - const stateStorage: IKbnUrlStateStorage = createKbnUrlStateStorage(); - const appStateFromUrl: DiscoverAppState | null = stateStorage.get('_a'); // create initial layer descriptor const hasTooltips = context?.contextualFields?.length && context?.contextualFields[0] !== '_source'; const linkUrlParams = { _a: rison.encode({ - filters: appStateFromUrl?.filters || [], - query: appStateFromUrl?.query, + filters: getQueryService().filterManager.getFilters() || [], + query: getQueryService().queryString.getQuery(), } as any), initialLayers: rison.encode({ id: uuid(), From 18cc4d54deabea5927d25c8153840d0a4cd3830a Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 6 Aug 2020 12:21:00 +0300 Subject: [PATCH 17/28] Building urls with urlGenerators --- .../public/actions/visualize_field_action.ts | 34 +++-- src/plugins/visualize/public/plugin.ts | 39 ++++- src/plugins/visualize/public/services.ts | 3 + .../visualize/public/url_generator.test.ts | 100 +++++++++++++ src/plugins/visualize/public/url_generator.ts | 137 ++++++++++++++++++ x-pack/plugins/maps/kibana.json | 3 +- .../plugins/maps/public/kibana_services.d.ts | 5 +- x-pack/plugins/maps/public/kibana_services.js | 8 +- x-pack/plugins/maps/public/plugin.ts | 28 +++- .../visualize_geo_field_action.ts | 55 ++++--- .../plugins/maps/public/url_generator.test.ts | 110 ++++++++++++++ x-pack/plugins/maps/public/url_generator.ts | 106 ++++++++++++++ 12 files changed, 576 insertions(+), 52 deletions(-) create mode 100644 src/plugins/visualize/public/url_generator.test.ts create mode 100644 src/plugins/visualize/public/url_generator.ts create mode 100644 x-pack/plugins/maps/public/url_generator.test.ts create mode 100644 x-pack/plugins/maps/public/url_generator.ts diff --git a/src/plugins/visualize/public/actions/visualize_field_action.ts b/src/plugins/visualize/public/actions/visualize_field_action.ts index 367c167150c75..b5f7447f207ea 100644 --- a/src/plugins/visualize/public/actions/visualize_field_action.ts +++ b/src/plugins/visualize/public/actions/visualize_field_action.ts @@ -16,11 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -import rison from 'rison-node'; -import { stringify } from 'query-string'; import { i18n } from '@kbn/i18n'; import { createAction } from '../../../ui_actions/public'; -import { getApplication, getUISettings, getIndexPatterns, getQueryService } from '../services'; +import { + getApplication, + getUISettings, + getIndexPatterns, + getQueryService, + getShareService, +} from '../services'; +import { VISUALIZE_APP_URL_GENERATOR, VisualizeUrlGeneratorState } from '../url_generator'; import { AGGS_TERMS_SIZE_SETTING } from '../../common/constants'; export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD'; @@ -60,21 +65,22 @@ export const visualizeFieldAction = createAction( }; } - const linkUrlParams = { - indexPattern: context.indexPatternId, + const generator = getShareService().urlGenerators.getUrlGenerator(VISUALIZE_APP_URL_GENERATOR); + const urlState: VisualizeUrlGeneratorState = { + filters: getQueryService().filterManager.getFilters(), + query: getQueryService().queryString.getQuery(), + indexPatternId: context.indexPatternId, type: 'histogram', - _a: rison.encode({ - filters: getQueryService().filterManager.getFilters() || [], - query: getQueryService().queryString.getQuery(), - vis: { - type: 'histogram', - aggs: [{ schema: 'metric', type: 'count', id: '1' }, agg], - }, - } as any), + vis: { + type: 'histogram', + aggs: [{ schema: 'metric', type: 'count', id: '1' }, agg], + }, }; + const url = await generator.createUrl(urlState); + const hash = url.split('#')[1]; getApplication().navigateToApp('visualize', { - path: `#/create?${stringify(linkUrlParams)}`, + path: `/#${hash}`, }); }, }); diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index c6169b073d231..d9b08d329f05e 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -34,7 +34,7 @@ import { import { Storage, createKbnUrlTracker, createKbnUrlStateStorage } from '../../kibana_utils/public'; import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../navigation/public'; -import { SharePluginStart } from '../../share/public'; +import { SharePluginStart, SharePluginSetup, UrlGeneratorState } from '../../share/public'; import { KibanaLegacySetup, KibanaLegacyStart } from '../../kibana_legacy/public'; import { VisualizationsStart } from '../../visualizations/public'; import { VisualizeConstants } from './application/visualize_constants'; @@ -48,8 +48,19 @@ import { VISUALIZE_FIELD_TRIGGER, VisualizeFieldContext, } from '../../ui_actions/public'; -import { setUISettings, setApplication, setIndexPatterns, setQueryService } from './services'; +import { + setUISettings, + setApplication, + setIndexPatterns, + setQueryService, + setShareService, +} from './services'; import { visualizeFieldAction, ACTION_VISUALIZE_FIELD } from './actions/visualize_field_action'; +import { + VisualizeUrlGeneratorState, + VISUALIZE_APP_URL_GENERATOR, + createVisualizeUrlGenerator, +} from './url_generator'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; @@ -66,6 +77,7 @@ export interface VisualizePluginSetupDependencies { home?: HomePublicPluginSetup; kibanaLegacy: KibanaLegacySetup; data: DataPublicPluginSetup; + share?: SharePluginSetup; } declare module '../../ui_actions/public' { @@ -74,6 +86,12 @@ declare module '../../ui_actions/public' { } } +declare module '../../share/public' { + export interface UrlGeneratorStateMapping { + [VISUALIZE_APP_URL_GENERATOR]: UrlGeneratorState; + } +} + export interface FeatureFlagConfig { showNewVisualizeFlow: boolean; } @@ -89,7 +107,7 @@ export class VisualizePlugin public async setup( core: CoreSetup, - { home, kibanaLegacy, data }: VisualizePluginSetupDependencies + { home, kibanaLegacy, data, share }: VisualizePluginSetupDependencies ) { const { appMounted, @@ -122,7 +140,17 @@ export class VisualizePlugin this.stopUrlTracking = () => { stopUrlTracker(); }; - + if (share) { + share.urlGenerators.registerUrlGenerator( + createVisualizeUrlGenerator(async () => { + const [coreStart] = await core.getStartServices(); + return { + appBasePath: coreStart.application.getUrlForApp('visualize'), + useHashedUrl: coreStart.uiSettings.get('state:storeInSessionStorage'), + }; + }) + ); + } setUISettings(core.uiSettings); core.application.register({ @@ -220,6 +248,9 @@ export class VisualizePlugin setApplication(core.application); setIndexPatterns(plugins.data.indexPatterns); setQueryService(plugins.data.query); + if (plugins.share) { + setShareService(plugins.share); + } plugins.uiActions.addTriggerAction(VISUALIZE_FIELD_TRIGGER, visualizeFieldAction); } diff --git a/src/plugins/visualize/public/services.ts b/src/plugins/visualize/public/services.ts index ed1baacce7ef3..8190872ec6508 100644 --- a/src/plugins/visualize/public/services.ts +++ b/src/plugins/visualize/public/services.ts @@ -20,11 +20,14 @@ import { ApplicationStart, IUiSettingsClient } from '../../../core/public'; import { createGetterSetter } from '../../../plugins/kibana_utils/public'; import { IndexPatternsContract, DataPublicPluginStart } from '../../../plugins/data/public'; +import { SharePluginStart } from '../../../plugins/share/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); export const [getApplication, setApplication] = createGetterSetter('Application'); +export const [getShareService, setShareService] = createGetterSetter('Share'); + export const [getIndexPatterns, setIndexPatterns] = createGetterSetter( 'IndexPatterns' ); diff --git a/src/plugins/visualize/public/url_generator.test.ts b/src/plugins/visualize/public/url_generator.test.ts new file mode 100644 index 0000000000000..8c8a0f70c15e3 --- /dev/null +++ b/src/plugins/visualize/public/url_generator.test.ts @@ -0,0 +1,100 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { createVisualizeUrlGenerator } from './url_generator'; +import { esFilters } from '../../data/public'; + +const APP_BASE_PATH: string = 'test/app/visualize'; +const VISUALIZE_ID: string = '13823000-99b9-11ea-9eb6-d9e8adceb647'; +const INDEXPATTERN_ID: string = '13823000-99b9-11ea-9eb6-d9e8adceb647'; + +describe('visualize url generator', () => { + test('creates a link to a new visualization', async () => { + const generator = createVisualizeUrlGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + }) + ); + const url = await generator.createUrl!({ indexPatternId: INDEXPATTERN_ID, type: 'table' }); + expect(url).toMatchInlineSnapshot( + `"test/app/visualize#/create?_g=()&_a=()&indexPattern=${INDEXPATTERN_ID}&type=table"` + ); + }); + + test('creates a link with global time range set up', async () => { + const generator = createVisualizeUrlGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + }) + ); + const url = await generator.createUrl!({ + timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, + indexPatternId: INDEXPATTERN_ID, + type: 'table', + }); + expect(url).toMatchInlineSnapshot( + `"test/app/visualize#/create?_g=(time:(from:now-15m,mode:relative,to:now))&_a=()&indexPattern=${INDEXPATTERN_ID}&type=table"` + ); + }); + + test('creates a link with filters, time range, refresh interval and query to a saved visualization', async () => { + const generator = createVisualizeUrlGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + indexPatternId: INDEXPATTERN_ID, + type: 'table', + }) + ); + const url = await generator.createUrl!({ + timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, + refreshInterval: { pause: false, value: 300 }, + visualizationId: VISUALIZE_ID, + filters: [ + { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'q1' }, + }, + { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'q1' }, + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + }, + ], + query: { query: 'q2', language: 'kuery' }, + indexPatternId: INDEXPATTERN_ID, + type: 'table', + }); + expect(url).toMatchInlineSnapshot( + `"test/app/visualize#/edit/${VISUALIZE_ID}?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!f,negate:!f),query:(query:q1))),refreshInterval:(pause:!f,value:300),time:(from:now-15m,mode:relative,to:now))&_a=(filters:!((meta:(alias:!n,disabled:!f,negate:!f),query:(query:q1))),query:(language:kuery,query:q2))&indexPattern=${INDEXPATTERN_ID}&type=table"` + ); + }); +}); diff --git a/src/plugins/visualize/public/url_generator.ts b/src/plugins/visualize/public/url_generator.ts new file mode 100644 index 0000000000000..38b7633c6fde1 --- /dev/null +++ b/src/plugins/visualize/public/url_generator.ts @@ -0,0 +1,137 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + TimeRange, + Filter, + Query, + esFilters, + QueryState, + RefreshInterval, +} from '../../data/public'; +import { setStateToKbnUrl } from '../../kibana_utils/public'; +import { UrlGeneratorsDefinition } from '../../share/public'; + +const STATE_STORAGE_KEY = '_a'; +const GLOBAL_STATE_STORAGE_KEY = '_g'; + +export const VISUALIZE_APP_URL_GENERATOR = 'VISUALIZE_APP_URL_GENERATOR'; + +export interface VisualizeUrlGeneratorState { + /** + * If given, it will load the given visualization else will load the create a new visualization page. + */ + visualizationId?: string; + /** + * Optionally set the time range in the time picker. + */ + timeRange?: TimeRange; + + /** + * Optional set indexPatternId. + */ + indexPatternId?: string; + + /** + * Optional set visualization type. + */ + type?: string; + + /** + * Optionally set the visualization. + */ + vis?: unknown; + + /** + * Optionally set the refresh interval. + */ + refreshInterval?: RefreshInterval; + + /** + * Optionally apply filers. NOTE: if given and used in conjunction with `dashboardId`, and the + * saved dashboard has filters saved with it, this will _replace_ those filters. + */ + filters?: Filter[]; + /** + * Optionally set a query. NOTE: if given and used in conjunction with `dashboardId`, and the + * saved dashboard 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; +} + +export const createVisualizeUrlGenerator = ( + getStartServices: () => Promise<{ + appBasePath: string; + useHashedUrl: boolean; + }> +): UrlGeneratorsDefinition => ({ + id: VISUALIZE_APP_URL_GENERATOR, + createUrl: async ({ + visualizationId, + filters, + indexPatternId, + query, + refreshInterval, + vis, + type, + timeRange, + hash, + }: VisualizeUrlGeneratorState): Promise => { + const startServices = await getStartServices(); + const useHash = hash ?? startServices.useHashedUrl; + const appBasePath = startServices.appBasePath; + const mode = visualizationId ? `edit/${visualizationId}` : `create`; + + const appState: { + query?: Query; + filters?: Filter[]; + vis?: unknown; + } = {}; + const queryState: QueryState = {}; + + if (query) appState.query = query; + if (filters && filters.length) + appState.filters = filters?.filter((f) => !esFilters.isFilterPinned(f)); + if (vis) appState.vis = vis; + + if (timeRange) queryState.time = timeRange; + if (filters && filters.length) + queryState.filters = filters?.filter((f) => esFilters.isFilterPinned(f)); + if (refreshInterval) queryState.refreshInterval = refreshInterval; + + let url = `${appBasePath}#/${mode}`; + url = setStateToKbnUrl(GLOBAL_STATE_STORAGE_KEY, queryState, { useHash }, url); + url = setStateToKbnUrl(STATE_STORAGE_KEY, appState, { useHash }, url); + + if (indexPatternId) { + url = `${url}&indexPattern=${indexPatternId}`; + } + + if (type) { + url = `${url}&type=${type}`; + } + + return url; + }, +}); diff --git a/x-pack/plugins/maps/kibana.json b/x-pack/plugins/maps/kibana.json index fbf45aee02125..d554d159a196f 100644 --- a/x-pack/plugins/maps/kibana.json +++ b/x-pack/plugins/maps/kibana.json @@ -15,7 +15,8 @@ "visualizations", "embeddable", "mapsLegacy", - "usageCollection" + "usageCollection", + "share" ], "ui": true, "server": true, diff --git a/x-pack/plugins/maps/public/kibana_services.d.ts b/x-pack/plugins/maps/public/kibana_services.d.ts index 4ae014318cd1f..bc74cc75aba2a 100644 --- a/x-pack/plugins/maps/public/kibana_services.d.ts +++ b/x-pack/plugins/maps/public/kibana_services.d.ts @@ -7,6 +7,7 @@ import { DataPublicPluginStart } from 'src/plugins/data/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { IndexPatternsService } from 'src/plugins/data/public/index_patterns'; import { ApplicationStart } from '../../../../src/core/public'; +import { SharePluginStart } from '../../../../src/plugins/share/public'; import { MapsConfigType } from '../config'; import { MapsLegacyConfigType } from '../../../../src/plugins/maps_legacy/public'; @@ -33,8 +34,8 @@ export function getCore(): any; export function getNavigation(): any; export function getCoreI18n(): any; export function getApplication(): ApplicationStart; +export function getShareService(): SharePluginStart; export function getSearchService(): DataPublicPluginStart['search']; -export function getQueryService(): DataPublicPluginStart['query']; export function getKibanaCommonConfig(): MapsLegacyConfigType; export function getMapAppConfig(): MapsConfigType; export function getIsEmsEnabled(): any; @@ -76,8 +77,8 @@ export function setCore(args: unknown): void; export function setNavigation(args: unknown): void; export function setCoreI18n(args: unknown): void; export function setApplication(args: ApplicationStart): void; +export function setShareService(args: SharePluginStart): void; export function setSearchService(args: DataPublicPluginStart['search']): void; -export function setQueryService(args: DataPublicPluginStart['query']): void; export function setKibanaCommonConfig(config: MapsLegacyConfigType): void; export function setMapAppConfig(config: MapsConfigType): void; export function setKibanaVersion(version: string): void; diff --git a/x-pack/plugins/maps/public/kibana_services.js b/x-pack/plugins/maps/public/kibana_services.js index f45a3769fdf8f..01e34164f2228 100644 --- a/x-pack/plugins/maps/public/kibana_services.js +++ b/x-pack/plugins/maps/public/kibana_services.js @@ -139,10 +139,6 @@ let dataSearchService; export const setSearchService = (searchService) => (dataSearchService = searchService); export const getSearchService = () => dataSearchService; -let dataQueryService; -export const setQueryService = (queryService) => (dataQueryService = queryService); -export const getQueryService = () => dataQueryService; - let kibanaVersion; export const setKibanaVersion = (version) => (kibanaVersion = version); export const getKibanaVersion = () => kibanaVersion; @@ -185,3 +181,7 @@ export const getIsGoldPlus = () => { let coreApplication; export const setApplication = (application) => (coreApplication = application); export const getApplication = () => coreApplication; + +let coreShareService; +export const setShareService = (shareService) => (coreShareService = shareService); +export const getShareService = () => coreShareService; diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 0b3c5076401c1..d3c4195d6f651 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -12,6 +12,7 @@ import { PluginInitializerContext, DEFAULT_APP_CATEGORIES, } from '../../../../src/core/public'; +import { SharePluginSetup, UrlGeneratorState } from '../../../../src/plugins/share/public'; // @ts-ignore import { MapView } from './inspector/views/map_view'; import { @@ -42,7 +43,7 @@ import { setUiSettings, setVisualizations, setApplication, - setQueryService, + setShareService, } from './kibana_services'; import { featureCatalogueEntry } from './feature_catalogue_entry'; // @ts-ignore @@ -53,6 +54,11 @@ import { VISUALIZE_GEO_FIELD_TRIGGER, VisualizeFieldContext, } from '../../../../src/plugins/ui_actions/public'; +import { + MapsUrlGeneratorState, + MAPS_APP_URL_GENERATOR, + createMapsUrlGenerator, +} from './url_generator'; import { APP_ICON, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public'; @@ -73,6 +79,7 @@ export interface MapsPluginSetupDependencies { visualizations: VisualizationsSetup; embeddable: EmbeddableSetup; mapsLegacy: { config: unknown }; + share: SharePluginSetup; } // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface MapsPluginStartDependencies {} @@ -83,13 +90,19 @@ declare module '../../../../src/plugins/ui_actions/public' { } } +declare module '../../../../src/plugins/share/public' { + export interface UrlGeneratorStateMapping { + [MAPS_APP_URL_GENERATOR]: UrlGeneratorState; + } +} + export const bindSetupCoreAndPlugins = ( core: CoreSetup, plugins: any, config: MapsConfigType, kibanaVersion: string ) => { - const { licensing, mapsLegacy } = plugins; + const { licensing, mapsLegacy, share } = plugins; const { uiSettings, http, notifications } = core; if (licensing) { licensing.license$.subscribe(({ uid }: { uid: string }) => setLicenseId(uid)); @@ -101,6 +114,15 @@ export const bindSetupCoreAndPlugins = ( setKibanaCommonConfig(mapsLegacy.config); setMapAppConfig(config); setKibanaVersion(kibanaVersion); + share.urlGenerators.registerUrlGenerator( + createMapsUrlGenerator(async () => { + const [coreStart] = await core.getStartServices(); + return { + appBasePath: coreStart.application.getUrlForApp('maps'), + useHashedUrl: coreStart.uiSettings.get('state:storeInSessionStorage'), + }; + }) + ); }; export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { @@ -119,7 +141,6 @@ export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { setIndexPatternSelect(data.ui.IndexPatternSelect); setTimeFilter(data.query.timefilter.timefilter); setSearchService(data.search); - setQueryService(data.query); setIndexPatternService(data.indexPatterns); setAutocompleteService(data.autocomplete); setCore(core); @@ -133,6 +154,7 @@ export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { setNavigation(plugins.navigation); setCoreI18n(core.i18n); setApplication(core.application); + setShareService(plugins.share); }; /** 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 ad9e0ac6a81ab..183332e2aaff8 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 @@ -4,12 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import rison from 'rison-node'; import uuid from 'uuid/v4'; -import { stringify } from 'query-string'; import { i18n } from '@kbn/i18n'; import { createAction } from '../../../../../src/plugins/ui_actions/public'; -import { getApplication, getIndexPatternService, getQueryService } from '../kibana_services'; +import { + getApplication, + getIndexPatternService, + getData, + getShareService, +} from '../kibana_services'; +import { MAPS_APP_URL_GENERATOR, MapsUrlGeneratorState } from '../url_generator'; +import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../../common/constants'; export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD'; @@ -17,7 +22,7 @@ export const visualizeGeoFieldAction = createAction i18n.translate('xpack.maps.discover.visualizeFieldLabel', { - defaultMessage: 'Visualize on a map', + defaultMessage: 'Visualize in Maps', }), execute: async (context) => { const indexPattern = await getIndexPatternService().get(context.indexPatternId); @@ -26,31 +31,33 @@ export const visualizeGeoFieldAction = createAction { + test('creates a link to a new visualization', async () => { + const generator = createMapsUrlGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + }) + ); + const url = await generator.createUrl!({}); + expect(url).toMatchInlineSnapshot(`"test/app/maps/map#/?_g=()&_a=()"`); + }); + + test('creates a link with global time range set up', async () => { + const generator = createMapsUrlGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + }) + ); + const url = await generator.createUrl!({ + timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, + }); + expect(url).toMatchInlineSnapshot( + `"test/app/maps/map#/?_g=(time:(from:now-15m,mode:relative,to:now))&_a=()"` + ); + }); + + test('creates a link with initialLayers set up', async () => { + const generator = createMapsUrlGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + }) + ); + const initialLayers = { + id: LAYER_ID, + visible: true, + type: LAYER_TYPE.VECTOR, + sourceDescriptor: { + id: LAYER_ID, + type: SOURCE_TYPES.ES_SEARCH, + tooltipProperties: [], + label: 'Sample Data', + indexPatternId: INDEX_PATTERN_ID, + geoField: 'test', + scalingType: SCALING_TYPES.LIMIT, + }, + }; + const url = await generator.createUrl!({ + initialLayers, + }); + expect(url).toMatchInlineSnapshot( + `"test/app/maps/map#/?_g=()&_a=()&initialLayers=(id:'${LAYER_ID}',sourceDescriptor:(geoField:test,id:'${LAYER_ID}',indexPatternId:'${INDEX_PATTERN_ID}',label:'Sample%20Data',scalingType:LIMIT,tooltipProperties:!(),type:ES_SEARCH),type:VECTOR,visible:!t)"` + ); + }); + + test('creates a link with filters, time range, refresh interval and query to a saved visualization', async () => { + const generator = createMapsUrlGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + }) + ); + const url = await generator.createUrl!({ + timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, + refreshInterval: { pause: false, value: 300 }, + mapId: MAP_ID, + filters: [ + { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'q1' }, + }, + { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'q1' }, + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + }, + ], + query: { query: 'q2', language: 'kuery' }, + }); + expect(url).toMatchInlineSnapshot( + `"test/app/maps/map#/${MAP_ID}?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!f,negate:!f),query:(query:q1))),refreshInterval:(pause:!f,value:300),time:(from:now-15m,mode:relative,to:now))&_a=(filters:!((meta:(alias:!n,disabled:!f,negate:!f),query:(query:q1))),query:(language:kuery,query:q2))"` + ); + }); +}); diff --git a/x-pack/plugins/maps/public/url_generator.ts b/x-pack/plugins/maps/public/url_generator.ts new file mode 100644 index 0000000000000..988c5e039128f --- /dev/null +++ b/x-pack/plugins/maps/public/url_generator.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { + TimeRange, + Filter, + Query, + esFilters, + QueryState, + RefreshInterval, +} from '../../../../src/plugins/data/public'; +import { setStateToKbnUrl } from '../../../../src/plugins/kibana_utils/public'; +import { UrlGeneratorsDefinition } from '../../../../src/plugins/share/public'; +import { LayerDescriptor } from '../common/descriptor_types/sources'; + +const STATE_STORAGE_KEY = '_a'; +const GLOBAL_STATE_STORAGE_KEY = '_g'; +const INITIAL_LAYERS_KEY = 'initialLayers'; + +export const MAPS_APP_URL_GENERATOR = 'MAPS_APP_URL_GENERATOR'; + +export interface MapsUrlGeneratorState { + /** + * 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; + + /** + * Optionally set the refresh interval. + */ + refreshInterval?: RefreshInterval; + + /** + * Optionally apply filers. NOTE: if given and used in conjunction with `dashboardId`, and the + * saved dashboard has filters saved with it, this will _replace_ those filters. + */ + filters?: Filter[]; + /** + * Optionally set a query. NOTE: if given and used in conjunction with `dashboardId`, and the + * saved dashboard 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; +} + +export const createMapsUrlGenerator = ( + getStartServices: () => Promise<{ + appBasePath: string; + useHashedUrl: boolean; + }> +): UrlGeneratorsDefinition => ({ + id: MAPS_APP_URL_GENERATOR, + createUrl: async ({ + mapId, + filters, + query, + refreshInterval, + timeRange, + initialLayers, + hash, + }: MapsUrlGeneratorState): Promise => { + const startServices = await getStartServices(); + const useHash = hash ?? startServices.useHashedUrl; + const appBasePath = startServices.appBasePath; + + const appState: { + query?: Query; + filters?: Filter[]; + vis?: unknown; + } = {}; + const queryState: QueryState = {}; + + if (query) appState.query = query; + if (filters && filters.length) + appState.filters = filters?.filter((f) => !esFilters.isFilterPinned(f)); + + if (timeRange) queryState.time = timeRange; + if (filters && filters.length) + queryState.filters = filters?.filter((f) => esFilters.isFilterPinned(f)); + if (refreshInterval) queryState.refreshInterval = refreshInterval; + + let url = `${appBasePath}/map#/${mapId || ''}`; + url = setStateToKbnUrl(GLOBAL_STATE_STORAGE_KEY, queryState, { useHash }, url); + url = setStateToKbnUrl(STATE_STORAGE_KEY, appState, { useHash }, url); + if (initialLayers) { + url = setStateToKbnUrl(INITIAL_LAYERS_KEY, initialLayers, { useHash }, url); + } + + return url; + }, +}); From b2da2dbdec50e52f09d50c78234e787bd0b0e1cc Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 6 Aug 2020 22:57:46 +0300 Subject: [PATCH 18/28] replace with constants, fetch initialLayers as array --- x-pack/plugins/maps/common/constants.ts | 1 + .../routing/bootstrap/get_initial_layers.js | 5 +-- .../visualize_geo_field_action.ts | 32 ++++++++++--------- .../plugins/maps/public/url_generator.test.ts | 32 +++++++++++-------- x-pack/plugins/maps/public/url_generator.ts | 13 +++++--- 5 files changed, 47 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index cf67ac4dd999f..0958786d4b959 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -26,6 +26,7 @@ export const EMS_TILES_VECTOR_TILE_PATH = 'vector/tile'; export const MAP_SAVED_OBJECT_TYPE = 'map'; export const APP_ID = 'maps'; export const APP_ICON = 'gisApp'; +export const INITIAL_LAYERS_KEY = 'initialLayers'; export const MAPS_APP_PATH = `app/${APP_ID}`; export const MAP_PATH = 'map'; diff --git a/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.js b/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.js index 5d02160fc3eb5..b47f83d5a6664 100644 --- a/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.js +++ b/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.js @@ -20,6 +20,7 @@ import { TileLayer } from '../../classes/layers/tile_layer/tile_layer'; import { EMSTMSSource } from '../../classes/sources/ems_tms_source'; import { VectorTileLayer } from '../../classes/layers/vector_tile_layer/vector_tile_layer'; import { getIsEmsEnabled, getToasts } from '../../kibana_services'; +import { INITIAL_LAYERS_KEY } from '../../../common/constants'; import { getKibanaTileMap } from '../../meta'; export function getInitialLayers(layerListJSON, initialLayers = []) { @@ -51,12 +52,12 @@ export function getInitialLayersFromUrlParam() { return []; } const mapAppParams = new URLSearchParams(locationSplit[1]); - if (!mapAppParams.has('initialLayers')) { + if (!mapAppParams.has(INITIAL_LAYERS_KEY)) { return []; } try { - let mapInitLayers = mapAppParams.get('initialLayers'); + let mapInitLayers = mapAppParams.get(INITIAL_LAYERS_KEY); if (mapInitLayers[mapInitLayers.length - 1] === '#') { mapInitLayers = mapInitLayers.substr(0, mapInitLayers.length - 1); } 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 183332e2aaff8..9a7f0ec043493 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 @@ -14,7 +14,7 @@ import { getShareService, } from '../kibana_services'; import { MAPS_APP_URL_GENERATOR, MapsUrlGeneratorState } from '../url_generator'; -import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../../common/constants'; +import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES, APP_ID, MAP_PATH } from '../../common/constants'; export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD'; @@ -31,20 +31,22 @@ export const visualizeGeoFieldAction = createAction { useHashedUrl: false, }) ); - const initialLayers = { - id: LAYER_ID, - visible: true, - type: LAYER_TYPE.VECTOR, - sourceDescriptor: { + const initialLayers = [ + { id: LAYER_ID, - type: SOURCE_TYPES.ES_SEARCH, - tooltipProperties: [], - label: 'Sample Data', - indexPatternId: INDEX_PATTERN_ID, - geoField: 'test', - scalingType: SCALING_TYPES.LIMIT, + visible: true, + type: LAYER_TYPE.VECTOR, + sourceDescriptor: { + id: LAYER_ID, + type: SOURCE_TYPES.ES_SEARCH, + tooltipProperties: [], + label: 'Sample Data', + indexPatternId: INDEX_PATTERN_ID, + geoField: 'test', + scalingType: SCALING_TYPES.LIMIT, + }, }, - }; + ]; + // @ts-ignore + const encodedLayers = rison.encode_array(initialLayers); const url = await generator.createUrl!({ initialLayers, }); expect(url).toMatchInlineSnapshot( - `"test/app/maps/map#/?_g=()&_a=()&initialLayers=(id:'${LAYER_ID}',sourceDescriptor:(geoField:test,id:'${LAYER_ID}',indexPatternId:'${INDEX_PATTERN_ID}',label:'Sample%20Data',scalingType:LIMIT,tooltipProperties:!(),type:ES_SEARCH),type:VECTOR,visible:!t)"` + `"test/app/maps/map#/?_g=()&_a=()&initialLayers=${encodedLayers}"` ); }); diff --git a/x-pack/plugins/maps/public/url_generator.ts b/x-pack/plugins/maps/public/url_generator.ts index 988c5e039128f..9e0398d18a3ed 100644 --- a/x-pack/plugins/maps/public/url_generator.ts +++ b/x-pack/plugins/maps/public/url_generator.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import rison from 'rison-node'; import { TimeRange, Filter, @@ -13,11 +14,11 @@ import { } from '../../../../src/plugins/data/public'; import { setStateToKbnUrl } from '../../../../src/plugins/kibana_utils/public'; import { UrlGeneratorsDefinition } from '../../../../src/plugins/share/public'; -import { LayerDescriptor } from '../common/descriptor_types/sources'; +import { LayerDescriptor } from '../common/descriptor_types'; +import { INITIAL_LAYERS_KEY } from '../common/constants'; const STATE_STORAGE_KEY = '_a'; const GLOBAL_STATE_STORAGE_KEY = '_g'; -const INITIAL_LAYERS_KEY = 'initialLayers'; export const MAPS_APP_URL_GENERATOR = 'MAPS_APP_URL_GENERATOR'; @@ -34,7 +35,7 @@ export interface MapsUrlGeneratorState { /** * Optionally set the initial Layers. */ - initialLayers?: LayerDescriptor; + initialLayers?: LayerDescriptor[]; /** * Optionally set the refresh interval. @@ -97,8 +98,10 @@ export const createMapsUrlGenerator = ( let url = `${appBasePath}/map#/${mapId || ''}`; url = setStateToKbnUrl(GLOBAL_STATE_STORAGE_KEY, queryState, { useHash }, url); url = setStateToKbnUrl(STATE_STORAGE_KEY, appState, { useHash }, url); - if (initialLayers) { - url = setStateToKbnUrl(INITIAL_LAYERS_KEY, initialLayers, { useHash }, url); + + if (initialLayers && initialLayers.length) { + // @ts-ignore + url = `${url}&${INITIAL_LAYERS_KEY}=${rison.encode_array(initialLayers)}`; } return url; From 5010fd82c28fce457f58c5e8fec463a7684c8584 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Sat, 8 Aug 2020 12:17:11 +0300 Subject: [PATCH 19/28] remove irrelevant comments --- x-pack/plugins/maps/public/url_generator.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/maps/public/url_generator.ts b/x-pack/plugins/maps/public/url_generator.ts index 9e0398d18a3ed..3fbb361342c7a 100644 --- a/x-pack/plugins/maps/public/url_generator.ts +++ b/x-pack/plugins/maps/public/url_generator.ts @@ -43,13 +43,13 @@ export interface MapsUrlGeneratorState { refreshInterval?: RefreshInterval; /** - * Optionally apply filers. NOTE: if given and used in conjunction with `dashboardId`, and the - * saved dashboard has filters saved with it, this will _replace_ those filters. + * 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 `dashboardId`, and the - * saved dashboard has a query saved with it, this will _replace_ that query. + * 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; /** From 4f09d9ecdc84f6861ddcff7dbb5f2fe858c3f0b7 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 12 Aug 2020 14:18:34 +0300 Subject: [PATCH 20/28] nice improvements --- .../components/sidebar/lib/visualize_trigger_utils.ts | 2 +- src/plugins/visualize/public/actions/visualize_field_action.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts index 96d95857a4127..4cb0c746e3d54 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts @@ -56,7 +56,7 @@ export function triggerVisualizeActions( fieldName: field.name, contextualFields: trigger === VISUALIZE_GEO_FIELD_TRIGGER ? contextualFields : [], }; - getUiActions().executeTriggerActions(trigger, triggerOptions); + getUiActions().getTrigger(trigger).exec(triggerOptions); } export async function isFieldVisualizable( diff --git a/src/plugins/visualize/public/actions/visualize_field_action.ts b/src/plugins/visualize/public/actions/visualize_field_action.ts index b5f7447f207ea..89c59820659b4 100644 --- a/src/plugins/visualize/public/actions/visualize_field_action.ts +++ b/src/plugins/visualize/public/actions/visualize_field_action.ts @@ -69,6 +69,7 @@ export const visualizeFieldAction = createAction( const urlState: VisualizeUrlGeneratorState = { filters: getQueryService().filterManager.getFilters(), query: getQueryService().queryString.getQuery(), + timeRange: getQueryService().timefilter.timefilter.getTime(), indexPatternId: context.indexPatternId, type: 'histogram', vis: { From b1e27bb5ebabb02c51708a9d2543fa9a29d15326 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 13 Aug 2020 14:47:19 +0300 Subject: [PATCH 21/28] Return contextualFields for both triggers --- .../components/sidebar/lib/visualize_trigger_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts index 4cb0c746e3d54..7545dba38b820 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts @@ -54,7 +54,7 @@ export function triggerVisualizeActions( const triggerOptions = { indexPatternId, fieldName: field.name, - contextualFields: trigger === VISUALIZE_GEO_FIELD_TRIGGER ? contextualFields : [], + contextualFields, }; getUiActions().getTrigger(trigger).exec(triggerOptions); } From 0f5b898653da0b49f8b539b2f8965522a9ec5c84 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 14 Aug 2020 11:02:03 +0300 Subject: [PATCH 22/28] Add getHref on actions, move capabilities to isCompatible method per action and other fixes --- .../sidebar/discover_field_details.tsx | 63 ++++++++---- .../sidebar/lib/visualize_trigger_utils.ts | 39 +++++--- src/plugins/ui_actions/public/index.ts | 2 + src/plugins/ui_actions/public/types.ts | 4 + src/plugins/visualize/common/constants.ts | 1 - .../public/actions/visualize_field_action.ts | 98 ++++++++++--------- src/plugins/visualize/public/plugin.ts | 14 +-- x-pack/plugins/maps/public/plugin.ts | 16 +-- .../visualize_geo_field_action.ts | 79 ++++++++------- 9 files changed, 181 insertions(+), 135 deletions(-) diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx index 9412312a24671..5f9f0b5ef9d0c 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx @@ -21,7 +21,11 @@ import { EuiLink, EuiIconTip, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { DiscoverFieldBucket } from './discover_field_bucket'; import { getWarnings } from './lib/get_warnings'; -import { triggerVisualizeActions, isFieldVisualizable } from './lib/visualize_trigger_utils'; +import { + triggerVisualizeActions, + isFieldVisualizable, + getVisualizeHref, +} from './lib/visualize_trigger_utils'; import { Bucket, FieldDetails } from './types'; import { IndexPatternField, IndexPattern } from '../../../../../data/public'; @@ -39,12 +43,22 @@ export function DiscoverFieldDetails({ onAddFilter, }: DiscoverFieldDetailsProps) { const warnings = getWarnings(field); - const [showVisualizeLink, setShowVisualizeLink] = useState(false); + const [showVisualizeLink, setShowVisualizeLink] = useState(false); + const [visualizeLink, setVisualizeLink] = useState(''); useEffect(() => { isFieldVisualizable(field, indexPattern.id, details.columns).then( (flag) => { setShowVisualizeLink(flag); + // get href only if Visualize button is enabled + getVisualizeHref(field, indexPattern.id, details.columns).then( + (uri) => { + setVisualizeLink(uri); + }, + () => { + setVisualizeLink(''); + } + ); }, () => { setShowVisualizeLink(false); @@ -52,6 +66,19 @@ export function DiscoverFieldDetails({ ); }, [field, indexPattern.id, details.columns]); + const handleVisualizeLinkClick = (event: React.MouseEvent) => { + if ( + !event.defaultPrevented && // onClick prevented default + event.button === 0 && // ignore everything but left clicks + (!event.currentTarget.target || event.currentTarget.target === '_self') && // let browser handle "target=_blank" etc. + !(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) // ignore clicks with modifier keys + ) { + // regular link click. let the uiActions code handle the navigation and show popup if needed + event.preventDefault(); + triggerVisualizeActions(field, indexPattern.id, details.columns); + } + }; + return (
{!details.error && ( @@ -89,23 +116,21 @@ export function DiscoverFieldDetails({ )} {showVisualizeLink && ( - <> - { - triggerVisualizeActions(field, indexPattern.id, details.columns); - }} - className="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall" - data-test-subj={`fieldVisualize-${field.name}`} - > - - {warnings.length > 0 && ( - - )} - - + // eslint-disable-next-line @elastic/eui/href-or-on-click + handleVisualizeLinkClick(e)} + href={visualizeLink} + className="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall" + data-test-subj={`fieldVisualize-${field.name}`} + > + + {warnings.length > 0 && ( + + )} + )}
); diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts index 7545dba38b820..d58080350a879 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts @@ -20,9 +20,8 @@ import { VISUALIZE_FIELD_TRIGGER, VISUALIZE_GEO_FIELD_TRIGGER, } from '../../../../../../ui_actions/public'; -import { getUiActions, getServices } from '../../../../kibana_services'; +import { getUiActions } from '../../../../kibana_services'; import { IndexPatternField, KBN_FIELD_TYPES } from '../../../../../../data/public'; -import { DiscoverServices } from '../../../../build_services'; function getTrigger(type: string) { return type === KBN_FIELD_TYPES.GEO_POINT || type === KBN_FIELD_TYPES.GEO_SHAPE @@ -34,9 +33,9 @@ async function getCompatibleActions( fieldName: string, indexPatternId: string, contextualFields: string[], - action: typeof VISUALIZE_FIELD_TRIGGER | typeof VISUALIZE_GEO_FIELD_TRIGGER + trigger: typeof VISUALIZE_FIELD_TRIGGER | typeof VISUALIZE_GEO_FIELD_TRIGGER ) { - const compatibleActions = await getUiActions().getTriggerCompatibleActions(action, { + const compatibleActions = await getUiActions().getTriggerCompatibleActions(trigger, { indexPatternId, fieldName, contextualFields, @@ -44,6 +43,28 @@ async function getCompatibleActions( return compatibleActions; } +export async function getVisualizeHref( + field: IndexPatternField, + indexPatternId: string | undefined, + contextualFields: string[] +) { + const trigger = getTrigger(field.type); + if (!indexPatternId) return ''; + const triggerOptions = { + indexPatternId, + fieldName: field.name, + contextualFields, + }; + const compatibleActions = await getCompatibleActions( + field.name, + indexPatternId, + contextualFields, + trigger + ); + // enable the link only if only one action is registered + return compatibleActions.length === 1 ? compatibleActions[0].getHref?.(triggerOptions) : ''; +} + export function triggerVisualizeActions( field: IndexPatternField, indexPatternId: string | undefined, @@ -64,20 +85,16 @@ export async function isFieldVisualizable( indexPatternId: string | undefined, contextualFields: string[] ) { - if (!indexPatternId) return; - if (field.name === '_id') { - // Else you'd get a 'Fielddata access on the _id field is disallowed' error on ES side. + if (field.name === '_id' || !indexPatternId) { + // for first condition you'd get a 'Fielddata access on the _id field is disallowed' error on ES side. return false; } const trigger = getTrigger(field.type); - const services: DiscoverServices = getServices(); const compatibleActions = await getCompatibleActions( field.name, indexPatternId, contextualFields, trigger ); - return ( - compatibleActions.length > 0 && field.visualizable && !!services.capabilities.visualize.show - ); + return compatibleActions.length > 0 && field.visualizable; } diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index 8b7889b902f94..fea940d599737 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -54,5 +54,7 @@ export { ActionContextMapping, ActionType, VisualizeFieldContext, + ACTION_VISUALIZE_FIELD, + ACTION_VISUALIZE_GEO_FIELD, } from './types'; export { ActionByType } from './actions'; diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index 31611a44530b4..0b5137a151359 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -56,8 +56,12 @@ export interface TriggerContextMapping { } const DEFAULT_ACTION = ''; +export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD'; +export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD'; export type ActionType = keyof ActionContextMapping; export interface ActionContextMapping { [DEFAULT_ACTION]: BaseContext; + [ACTION_VISUALIZE_FIELD]: VisualizeFieldContext; + [ACTION_VISUALIZE_GEO_FIELD]: VisualizeFieldContext; } diff --git a/src/plugins/visualize/common/constants.ts b/src/plugins/visualize/common/constants.ts index bbb70c652aa31..4e33638286a19 100644 --- a/src/plugins/visualize/common/constants.ts +++ b/src/plugins/visualize/common/constants.ts @@ -17,5 +17,4 @@ * under the License. */ -export const VISUALIZE_ENABLE_LABS_SETTING = 'visualize:enableLabs'; export const AGGS_TERMS_SIZE_SETTING = 'discover:aggs:terms:size'; diff --git a/src/plugins/visualize/public/actions/visualize_field_action.ts b/src/plugins/visualize/public/actions/visualize_field_action.ts index 89c59820659b4..a5772d099f5ec 100644 --- a/src/plugins/visualize/public/actions/visualize_field_action.ts +++ b/src/plugins/visualize/public/actions/visualize_field_action.ts @@ -17,7 +17,11 @@ * under the License. */ import { i18n } from '@kbn/i18n'; -import { createAction } from '../../../ui_actions/public'; +import { + createAction, + ACTION_VISUALIZE_FIELD, + VisualizeFieldContext, +} from '../../../ui_actions/public'; import { getApplication, getUISettings, @@ -28,56 +32,19 @@ import { import { VISUALIZE_APP_URL_GENERATOR, VisualizeUrlGeneratorState } from '../url_generator'; import { AGGS_TERMS_SIZE_SETTING } from '../../common/constants'; -export const ACTION_VISUALIZE_FIELD = 'ACTION_VISUALIZE_FIELD'; - export const visualizeFieldAction = createAction({ type: ACTION_VISUALIZE_FIELD, getDisplayName: () => i18n.translate('visualize.discover.visualizeFieldLabel', { defaultMessage: 'Visualize on Vis Editor', }), + isCompatible: async () => !!getApplication().capabilities.visualize.show, + getHref: async (context) => { + const url = await getVisualizeUrl(context); + return url; + }, execute: async (context) => { - const indexPattern = await getIndexPatterns().get(context.indexPatternId); - const field = indexPattern.fields.find((fld) => fld.name === context.fieldName); - const aggsTermSize = getUISettings().get(AGGS_TERMS_SIZE_SETTING); - let agg; - - // If we're visualizing a date field, and our index is time based (and thus has a time filter), - // then run a date histogram - if (field?.type === 'date' && indexPattern.timeFieldName === context.fieldName) { - agg = { - type: 'date_histogram', - schema: 'segment', - params: { - field: context.fieldName, - interval: 'auto', - }, - }; - } else { - agg = { - type: 'terms', - schema: 'segment', - params: { - field: context.fieldName, - size: parseInt(aggsTermSize, 10), - orderBy: '1', - }, - }; - } - - const generator = getShareService().urlGenerators.getUrlGenerator(VISUALIZE_APP_URL_GENERATOR); - const urlState: VisualizeUrlGeneratorState = { - filters: getQueryService().filterManager.getFilters(), - query: getQueryService().queryString.getQuery(), - timeRange: getQueryService().timefilter.timefilter.getTime(), - indexPatternId: context.indexPatternId, - type: 'histogram', - vis: { - type: 'histogram', - aggs: [{ schema: 'metric', type: 'count', id: '1' }, agg], - }, - }; - const url = await generator.createUrl(urlState); + const url = await getVisualizeUrl(context); const hash = url.split('#')[1]; getApplication().navigateToApp('visualize', { @@ -85,3 +52,46 @@ export const visualizeFieldAction = createAction( }); }, }); + +const getVisualizeUrl = async (context: VisualizeFieldContext) => { + const indexPattern = await getIndexPatterns().get(context.indexPatternId); + const field = indexPattern.fields.find((fld) => fld.name === context.fieldName); + const aggsTermSize = getUISettings().get(AGGS_TERMS_SIZE_SETTING); + let agg; + + // If we're visualizing a date field, and our index is time based (and thus has a time filter), + // then run a date histogram + if (field?.type === 'date' && indexPattern.timeFieldName === context.fieldName) { + agg = { + type: 'date_histogram', + schema: 'segment', + params: { + field: context.fieldName, + interval: 'auto', + }, + }; + } else { + agg = { + type: 'terms', + schema: 'segment', + params: { + field: context.fieldName, + size: parseInt(aggsTermSize, 10), + orderBy: '1', + }, + }; + } + const generator = getShareService().urlGenerators.getUrlGenerator(VISUALIZE_APP_URL_GENERATOR); + const urlState: VisualizeUrlGeneratorState = { + filters: getQueryService().filterManager.getFilters(), + query: getQueryService().queryString.getQuery(), + timeRange: getQueryService().timefilter.timefilter.getTime(), + indexPatternId: context.indexPatternId, + type: 'histogram', + vis: { + type: 'histogram', + aggs: [{ schema: 'metric', type: 'count', id: '1' }, agg], + }, + }; + return generator.createUrl(urlState); +}; diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 0c014f935dae1..54b3ba3c6319b 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -48,11 +48,7 @@ import { VisualizeServices } from './application/types'; import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; import { SavedObjectsStart } from '../../saved_objects/public'; import { EmbeddableStart } from '../../embeddable/public'; -import { - UiActionsStart, - VISUALIZE_FIELD_TRIGGER, - VisualizeFieldContext, -} from '../../ui_actions/public'; +import { UiActionsStart, VISUALIZE_FIELD_TRIGGER } from '../../ui_actions/public'; import { setUISettings, setApplication, @@ -60,7 +56,7 @@ import { setQueryService, setShareService, } from './services'; -import { visualizeFieldAction, ACTION_VISUALIZE_FIELD } from './actions/visualize_field_action'; +import { visualizeFieldAction } from './actions/visualize_field_action'; import { VisualizeUrlGeneratorState, VISUALIZE_APP_URL_GENERATOR, @@ -85,12 +81,6 @@ export interface VisualizePluginSetupDependencies { share?: SharePluginSetup; } -declare module '../../ui_actions/public' { - export interface ActionContextMapping { - [ACTION_VISUALIZE_FIELD]: VisualizeFieldContext; - } -} - declare module '../../share/public' { export interface UrlGeneratorStateMapping { [VISUALIZE_APP_URL_GENERATOR]: UrlGeneratorState; diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 51cc95052ddad..63cf23d969f22 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -52,10 +52,7 @@ import { featureCatalogueEntry } from './feature_catalogue_entry'; import { getMapsVisTypeAlias } from './maps_vis_type_alias'; import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public'; -import { - VISUALIZE_GEO_FIELD_TRIGGER, - VisualizeFieldContext, -} from '../../../../src/plugins/ui_actions/public'; +import { VISUALIZE_GEO_FIELD_TRIGGER } from '../../../../src/plugins/ui_actions/public'; import { MapsUrlGeneratorState, MAPS_APP_URL_GENERATOR, @@ -70,10 +67,7 @@ import { ILicense } from '../../licensing/common/types'; import { lazyLoadMapModules } from './lazy_load_bundle'; import { MapsStartApi } from './api'; import { createSecurityLayerDescriptors, registerLayerWizard, registerSource } from './api'; -import { - visualizeGeoFieldAction, - ACTION_VISUALIZE_GEO_FIELD, -} from './trigger_actions/visualize_geo_field_action'; +import { visualizeGeoFieldAction } from './trigger_actions/visualize_geo_field_action'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; @@ -86,12 +80,6 @@ export interface MapsPluginSetupDependencies { // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface MapsPluginStartDependencies {} -declare module '../../../../src/plugins/ui_actions/public' { - export interface ActionContextMapping { - [ACTION_VISUALIZE_GEO_FIELD]: VisualizeFieldContext; - } -} - declare module '../../../../src/plugins/share/public' { export interface UrlGeneratorStateMapping { [MAPS_APP_URL_GENERATOR]: UrlGeneratorState; 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 9a7f0ec043493..aa7225b620ef6 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 @@ -6,7 +6,11 @@ import uuid from 'uuid/v4'; import { i18n } from '@kbn/i18n'; -import { createAction } from '../../../../../src/plugins/ui_actions/public'; +import { + createAction, + ACTION_VISUALIZE_GEO_FIELD, + VisualizeFieldContext, +} from '../../../../../src/plugins/ui_actions/public'; import { getApplication, getIndexPatternService, @@ -16,46 +20,19 @@ import { import { MAPS_APP_URL_GENERATOR, MapsUrlGeneratorState } from '../url_generator'; import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES, APP_ID, MAP_PATH } from '../../common/constants'; -export const ACTION_VISUALIZE_GEO_FIELD = 'ACTION_VISUALIZE_GEO_FIELD'; - export const visualizeGeoFieldAction = createAction({ type: ACTION_VISUALIZE_GEO_FIELD, getDisplayName: () => i18n.translate('xpack.maps.discover.visualizeFieldLabel', { defaultMessage: 'Visualize in Maps', }), + isCompatible: async () => !!getApplication().capabilities.visualize.show, + getHref: async (context) => { + const url = await getMapsLink(context); + return url; + }, execute: async (context) => { - const indexPattern = await getIndexPatternService().get(context.indexPatternId); - const field = indexPattern.fields.find((fld) => fld.name === context.fieldName); - const supportsClustering = field?.aggregatable; - // create initial layer descriptor - const hasTooltips = - context?.contextualFields?.length && context?.contextualFields[0] !== '_source'; - const initialLayers = [ - { - id: uuid(), - visible: true, - type: supportsClustering ? LAYER_TYPE.BLENDED_VECTOR : LAYER_TYPE.VECTOR, - sourceDescriptor: { - id: uuid(), - type: SOURCE_TYPES.ES_SEARCH, - tooltipProperties: hasTooltips ? context.contextualFields : [], - label: indexPattern.title, - indexPatternId: context.indexPatternId, - geoField: context.fieldName, - scalingType: supportsClustering ? SCALING_TYPES.CLUSTERS : SCALING_TYPES.LIMIT, - }, - }, - ]; - - const generator = getShareService().urlGenerators.getUrlGenerator(MAPS_APP_URL_GENERATOR); - const urlState: MapsUrlGeneratorState = { - filters: getData().query.filterManager.getFilters(), - query: getData().query.queryString.getQuery(), - initialLayers, - timeRange: getData().query.timefilter.timefilter.getTime(), - }; - const url = await generator.createUrl(urlState); + const url = await getMapsLink(context); const hash = url.split('#')[1]; getApplication().navigateToApp(APP_ID, { @@ -63,3 +40,37 @@ export const visualizeGeoFieldAction = createAction { + const indexPattern = await getIndexPatternService().get(context.indexPatternId); + const field = indexPattern.fields.find((fld) => fld.name === context.fieldName); + const supportsClustering = field?.aggregatable; + // create initial layer descriptor + const hasTooltips = + context?.contextualFields?.length && context?.contextualFields[0] !== '_source'; + const initialLayers = [ + { + id: uuid(), + visible: true, + type: supportsClustering ? LAYER_TYPE.BLENDED_VECTOR : LAYER_TYPE.VECTOR, + sourceDescriptor: { + id: uuid(), + type: SOURCE_TYPES.ES_SEARCH, + tooltipProperties: hasTooltips ? context.contextualFields : [], + label: indexPattern.title, + indexPatternId: context.indexPatternId, + geoField: context.fieldName, + scalingType: supportsClustering ? SCALING_TYPES.CLUSTERS : SCALING_TYPES.LIMIT, + }, + }, + ]; + + const generator = getShareService().urlGenerators.getUrlGenerator(MAPS_APP_URL_GENERATOR); + const urlState: MapsUrlGeneratorState = { + filters: getData().query.filterManager.getFilters(), + query: getData().query.queryString.getQuery(), + initialLayers, + timeRange: getData().query.timefilter.timefilter.getTime(), + }; + return generator.createUrl(urlState); +}; From 86bf980e0a5080c4b5bcd24d6ec65d4e0065d643 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 14 Aug 2020 14:28:05 +0300 Subject: [PATCH 23/28] fix type --- src/plugins/ui_actions/public/types.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index c7f2116ea3c00..b00f4628ffb96 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -40,8 +40,6 @@ export interface VisualizeFieldContext { contextualFields?: string[]; } -const DEFAULT_TRIGGER = ''; - export type TriggerId = keyof TriggerContextMapping; export type BaseContext = object; From 389fd4c85afb83e02ceb36b0ed926655f465c6c6 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 14 Aug 2020 15:16:29 +0300 Subject: [PATCH 24/28] Fix type incompatibility after merging with master --- .../sidebar/lib/visualize_trigger_utils.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts index d58080350a879..6c68f781a09c4 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts @@ -19,16 +19,24 @@ import { VISUALIZE_FIELD_TRIGGER, VISUALIZE_GEO_FIELD_TRIGGER, + visualizeFieldTrigger, + visualizeGeoFieldTrigger, } from '../../../../../../ui_actions/public'; import { getUiActions } from '../../../../kibana_services'; import { IndexPatternField, KBN_FIELD_TYPES } from '../../../../../../data/public'; -function getTrigger(type: string) { +function getTriggerConstant(type: string) { return type === KBN_FIELD_TYPES.GEO_POINT || type === KBN_FIELD_TYPES.GEO_SHAPE ? VISUALIZE_GEO_FIELD_TRIGGER : VISUALIZE_FIELD_TRIGGER; } +function getTrigger(type: string) { + return type === KBN_FIELD_TYPES.GEO_POINT || type === KBN_FIELD_TYPES.GEO_SHAPE + ? visualizeGeoFieldTrigger + : visualizeFieldTrigger; +} + async function getCompatibleActions( fieldName: string, indexPatternId: string, @@ -48,18 +56,18 @@ export async function getVisualizeHref( indexPatternId: string | undefined, contextualFields: string[] ) { - const trigger = getTrigger(field.type); if (!indexPatternId) return ''; const triggerOptions = { indexPatternId, fieldName: field.name, contextualFields, + trigger: getTrigger(field.type), }; const compatibleActions = await getCompatibleActions( field.name, indexPatternId, contextualFields, - trigger + getTriggerConstant(field.type) ); // enable the link only if only one action is registered return compatibleActions.length === 1 ? compatibleActions[0].getHref?.(triggerOptions) : ''; @@ -71,7 +79,7 @@ export function triggerVisualizeActions( contextualFields: string[] ) { if (!indexPatternId) return; - const trigger = getTrigger(field.type); + const trigger = getTriggerConstant(field.type); const triggerOptions = { indexPatternId, fieldName: field.name, @@ -89,7 +97,7 @@ export async function isFieldVisualizable( // for first condition you'd get a 'Fielddata access on the _id field is disallowed' error on ES side. return false; } - const trigger = getTrigger(field.type); + const trigger = getTriggerConstant(field.type); const compatibleActions = await getCompatibleActions( field.name, indexPatternId, From 33777d021dac502805165ee03df74291d2c6cce5 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 18 Aug 2020 10:21:09 +0300 Subject: [PATCH 25/28] fixes on maps plugin file after merge --- x-pack/plugins/maps/public/plugin.ts | 113 +++++++-------------------- 1 file changed, 28 insertions(+), 85 deletions(-) diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index b60811a5c930c..b793e26ecd86e 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -15,7 +15,6 @@ import { PluginInitializerContext, DEFAULT_APP_CATEGORIES, } from '../../../../src/core/public'; -import { SharePluginSetup, UrlGeneratorState } from '../../../../src/plugins/share/public'; // @ts-ignore import { MapView } from './inspector/views/map_view'; import { @@ -24,33 +23,23 @@ import { setKibanaVersion, setLicenseId, setMapAppConfig, - setMapsCapabilities, - setNavigation, - setSavedObjectsClient, - setSearchService, - setTimeFilter, - setToasts, - setUiActions, - setUiSettings, - setVisualizations, + setStartServices, setApplication, setShareService, - setEmbeddableService, - setNavigateToApp, - setStartServices, } from './kibana_services'; import { featureCatalogueEntry } from './feature_catalogue_entry'; // @ts-ignore import { getMapsVisTypeAlias } from './maps_vis_type_alias'; import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public'; +import { APP_ICON, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; import { VISUALIZE_GEO_FIELD_TRIGGER } from '../../../../src/plugins/ui_actions/public'; import { MapsUrlGeneratorState, MAPS_APP_URL_GENERATOR, createMapsUrlGenerator, } from './url_generator'; -import { APP_ICON, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; +import { visualizeGeoFieldAction } from './trigger_actions/visualize_geo_field_action'; import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public'; import { MapsXPackConfig, MapsConfigType } from '../config'; @@ -59,7 +48,11 @@ import { ILicense } from '../../licensing/common/types'; import { lazyLoadMapModules } from './lazy_load_bundle'; import { MapsStartApi } from './api'; import { createSecurityLayerDescriptors, registerLayerWizard, registerSource } from './api'; -import { visualizeGeoFieldAction } from './trigger_actions/visualize_geo_field_action'; +import { + SharePluginSetup, + UrlGeneratorState, + SharePluginStart, +} from '../../../../src/plugins/share/public'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { MapsLegacyConfigType } from '../../../../src/plugins/maps_legacy/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; @@ -71,78 +64,10 @@ export interface MapsPluginSetupDependencies { home: HomePublicPluginSetup; visualizations: VisualizationsSetup; embeddable: EmbeddableSetup; - share: SharePluginSetup; mapsLegacy: { config: MapsLegacyConfigType }; + share: SharePluginSetup; } -declare module '../../../../src/plugins/share/public' { - export interface UrlGeneratorStateMapping { - [MAPS_APP_URL_GENERATOR]: UrlGeneratorState; - } -} - -export const bindSetupCoreAndPlugins = ( - core: CoreSetup, - plugins: any, - config: MapsConfigType, - kibanaVersion: string -) => { - const { licensing, mapsLegacy, share } = plugins; - const { uiSettings, http, notifications } = core; - if (licensing) { - licensing.license$.subscribe(({ uid }: { uid: string }) => setLicenseId(uid)); - } - setHttp(http); - setToasts(notifications.toasts); - setVisualizations(plugins.visualizations); - setUiSettings(uiSettings); - setKibanaCommonConfig(mapsLegacy.config); - setMapAppConfig(config); - setKibanaVersion(kibanaVersion); - share.urlGenerators.registerUrlGenerator( - createMapsUrlGenerator(async () => { - const [coreStart] = await core.getStartServices(); - return { - appBasePath: coreStart.application.getUrlForApp('maps'), - useHashedUrl: coreStart.uiSettings.get('state:storeInSessionStorage'), - }; - }) - ); -}; - -export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { - const { fileUpload, data, inspector, licensing } = plugins; - if (licensing) { - licensing.license$.subscribe((license: ILicense) => { - const gold = license.check(APP_ID, 'gold'); - setIsGoldPlus(gold.state === 'valid'); - }); - } - - plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction); - - setInspector(inspector); - setFileUpload(fileUpload); - setIndexPatternSelect(data.ui.IndexPatternSelect); - setTimeFilter(data.query.timefilter.timefilter); - setSearchService(data.search); - setIndexPatternService(data.indexPatterns); - setAutocompleteService(data.autocomplete); - setCore(core); - setSavedObjectsClient(core.savedObjects.client); - setCoreChrome(core.chrome); - setCoreOverlays(core.overlays); - setMapsCapabilities(core.application.capabilities.maps); - setDocLinks(core.docLinks); - setData(plugins.data); - setUiActions(plugins.uiActions); - setNavigation(plugins.navigation); - setCoreI18n(core.i18n); - setApplication(core.application); - setShareService(plugins.share); - setEmbeddableService(plugins.embeddable); - setNavigateToApp(core.application.navigateToApp); -}; export interface MapsPluginStartDependencies { data: DataPublicPluginStart; embeddable: EmbeddableStart; @@ -151,6 +76,13 @@ export interface MapsPluginStartDependencies { licensing: LicensingPluginStart; navigation: NavigationPublicPluginStart; uiActions: UiActionsStart; + share: SharePluginStart; +} + +declare module '../../../../src/plugins/share/public' { + export interface UrlGeneratorStateMapping { + [MAPS_APP_URL_GENERATOR]: UrlGeneratorState; + } } /** @@ -181,6 +113,15 @@ export class MapsPlugin setKibanaCommonConfig(plugins.mapsLegacy.config); setMapAppConfig(config); setKibanaVersion(this._initializerContext.env.packageInfo.version); + plugins.share.urlGenerators.registerUrlGenerator( + createMapsUrlGenerator(async () => { + const [coreStart] = await core.getStartServices(); + return { + appBasePath: coreStart.application.getUrlForApp('maps'), + useHashedUrl: coreStart.uiSettings.get('state:storeInSessionStorage'), + }; + }) + ); plugins.inspector.registerView(MapView); plugins.home.featureCatalogue.register(featureCatalogueEntry); @@ -212,7 +153,9 @@ export class MapsPlugin setLicenseId(license.uid); }); } - + plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction); + setApplication(core.application); + setShareService(plugins.share); setStartServices(core, plugins); return { From 51bf61b45216900d25bfbf5fbe290189f3a79027 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 18 Aug 2020 10:57:56 +0300 Subject: [PATCH 26/28] remove unecessary declarations --- src/plugins/visualize/public/plugin.ts | 14 ++------------ x-pack/plugins/maps/public/plugin.ts | 18 ++---------------- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 54b3ba3c6319b..8794593d6c958 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -39,7 +39,7 @@ import { } from '../../kibana_utils/public'; import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../navigation/public'; -import { SharePluginStart, SharePluginSetup, UrlGeneratorState } from '../../share/public'; +import { SharePluginStart, SharePluginSetup } from '../../share/public'; import { KibanaLegacySetup, KibanaLegacyStart } from '../../kibana_legacy/public'; import { VisualizationsStart } from '../../visualizations/public'; import { VisualizeConstants } from './application/visualize_constants'; @@ -57,11 +57,7 @@ import { setShareService, } from './services'; import { visualizeFieldAction } from './actions/visualize_field_action'; -import { - VisualizeUrlGeneratorState, - VISUALIZE_APP_URL_GENERATOR, - createVisualizeUrlGenerator, -} from './url_generator'; +import { createVisualizeUrlGenerator } from './url_generator'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; @@ -81,12 +77,6 @@ export interface VisualizePluginSetupDependencies { share?: SharePluginSetup; } -declare module '../../share/public' { - export interface UrlGeneratorStateMapping { - [VISUALIZE_APP_URL_GENERATOR]: UrlGeneratorState; - } -} - export interface FeatureFlagConfig { showNewVisualizeFlow: boolean; } diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index b793e26ecd86e..93568f9010968 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -34,11 +34,7 @@ import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public'; import { APP_ICON, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; import { VISUALIZE_GEO_FIELD_TRIGGER } from '../../../../src/plugins/ui_actions/public'; -import { - MapsUrlGeneratorState, - MAPS_APP_URL_GENERATOR, - createMapsUrlGenerator, -} from './url_generator'; +import { createMapsUrlGenerator } from './url_generator'; import { visualizeGeoFieldAction } from './trigger_actions/visualize_geo_field_action'; import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public'; @@ -48,11 +44,7 @@ import { ILicense } from '../../licensing/common/types'; import { lazyLoadMapModules } from './lazy_load_bundle'; import { MapsStartApi } from './api'; import { createSecurityLayerDescriptors, registerLayerWizard, registerSource } from './api'; -import { - SharePluginSetup, - UrlGeneratorState, - SharePluginStart, -} from '../../../../src/plugins/share/public'; +import { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { MapsLegacyConfigType } from '../../../../src/plugins/maps_legacy/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; @@ -79,12 +71,6 @@ export interface MapsPluginStartDependencies { share: SharePluginStart; } -declare module '../../../../src/plugins/share/public' { - export interface UrlGeneratorStateMapping { - [MAPS_APP_URL_GENERATOR]: UrlGeneratorState; - } -} - /** * These are the interfaces with your public contracts. You should export these * for other plugins to use in _their_ `SetupDeps`/`StartDeps` interfaces. From dfe10830f971db66443b440634b22c94e5a39be0 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 18 Aug 2020 16:55:45 +0300 Subject: [PATCH 27/28] nice improvements --- .../sidebar/discover_field_details.tsx | 17 ++++++----------- .../sidebar/lib/visualize_trigger_utils.ts | 6 ++++-- .../public/actions/visualize_field_action.ts | 2 +- .../plugins/maps/public/url_generator.test.ts | 1 - x-pack/typings/rison_node.d.ts | 3 +++ 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx index 5f9f0b5ef9d0c..141e333956094 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field_details.tsx @@ -17,7 +17,7 @@ * under the License. */ import React, { useState, useEffect } from 'react'; -import { EuiLink, EuiIconTip, EuiText } from '@elastic/eui'; +import { EuiLink, EuiIconTip, EuiText, EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { DiscoverFieldBucket } from './discover_field_bucket'; import { getWarnings } from './lib/get_warnings'; @@ -44,7 +44,7 @@ export function DiscoverFieldDetails({ }: DiscoverFieldDetailsProps) { const warnings = getWarnings(field); const [showVisualizeLink, setShowVisualizeLink] = useState(false); - const [visualizeLink, setVisualizeLink] = useState(''); + const [visualizeLink, setVisualizeLink] = useState(''); useEffect(() => { isFieldVisualizable(field, indexPattern.id, details.columns).then( @@ -53,7 +53,7 @@ export function DiscoverFieldDetails({ // get href only if Visualize button is enabled getVisualizeHref(field, indexPattern.id, details.columns).then( (uri) => { - setVisualizeLink(uri); + if (uri) setVisualizeLink(uri); }, () => { setVisualizeLink(''); @@ -67,12 +67,7 @@ export function DiscoverFieldDetails({ }, [field, indexPattern.id, details.columns]); const handleVisualizeLinkClick = (event: React.MouseEvent) => { - if ( - !event.defaultPrevented && // onClick prevented default - event.button === 0 && // ignore everything but left clicks - (!event.currentTarget.target || event.currentTarget.target === '_self') && // let browser handle "target=_blank" etc. - !(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) // ignore clicks with modifier keys - ) { + if (!event.defaultPrevented) { // regular link click. let the uiActions code handle the navigation and show popup if needed event.preventDefault(); triggerVisualizeActions(field, indexPattern.id, details.columns); @@ -117,7 +112,7 @@ export function DiscoverFieldDetails({ {showVisualizeLink && ( // eslint-disable-next-line @elastic/eui/href-or-on-click - handleVisualizeLinkClick(e)} href={visualizeLink} className="kuiButton kuiButton--secondary kuiButton--small kuiVerticalRhythmSmall" @@ -130,7 +125,7 @@ export function DiscoverFieldDetails({ {warnings.length > 0 && ( )} - + )}
); diff --git a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts index 6c68f781a09c4..f058c198cae7f 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/visualize_trigger_utils.ts @@ -56,7 +56,7 @@ export async function getVisualizeHref( indexPatternId: string | undefined, contextualFields: string[] ) { - if (!indexPatternId) return ''; + if (!indexPatternId) return undefined; const triggerOptions = { indexPatternId, fieldName: field.name, @@ -70,7 +70,9 @@ export async function getVisualizeHref( getTriggerConstant(field.type) ); // enable the link only if only one action is registered - return compatibleActions.length === 1 ? compatibleActions[0].getHref?.(triggerOptions) : ''; + return compatibleActions.length === 1 + ? compatibleActions[0].getHref?.(triggerOptions) + : undefined; } export function triggerVisualizeActions( diff --git a/src/plugins/visualize/public/actions/visualize_field_action.ts b/src/plugins/visualize/public/actions/visualize_field_action.ts index a5772d099f5ec..6671d2c981910 100644 --- a/src/plugins/visualize/public/actions/visualize_field_action.ts +++ b/src/plugins/visualize/public/actions/visualize_field_action.ts @@ -36,7 +36,7 @@ export const visualizeFieldAction = createAction( type: ACTION_VISUALIZE_FIELD, getDisplayName: () => i18n.translate('visualize.discover.visualizeFieldLabel', { - defaultMessage: 'Visualize on Vis Editor', + defaultMessage: 'Visualize field', }), isCompatible: async () => !!getApplication().capabilities.visualize.show, getHref: async (context) => { diff --git a/x-pack/plugins/maps/public/url_generator.test.ts b/x-pack/plugins/maps/public/url_generator.test.ts index ce0b1555c9eb9..a44f8d952fde1 100644 --- a/x-pack/plugins/maps/public/url_generator.test.ts +++ b/x-pack/plugins/maps/public/url_generator.test.ts @@ -63,7 +63,6 @@ describe('visualize url generator', () => { }, }, ]; - // @ts-ignore const encodedLayers = rison.encode_array(initialLayers); const url = await generator.createUrl!({ initialLayers, diff --git a/x-pack/typings/rison_node.d.ts b/x-pack/typings/rison_node.d.ts index 295392af2e05b..0e6069147e66f 100644 --- a/x-pack/typings/rison_node.d.ts +++ b/x-pack/typings/rison_node.d.ts @@ -23,4 +23,7 @@ declare module 'rison-node' { // eslint-disable-next-line @typescript-eslint/naming-convention export const encode_object: (input: Input) => string; + + // eslint-disable-next-line @typescript-eslint/naming-convention + export const encode_array: (input: Input) => string; } From c8fd6cc4614dcf43e195b50a9f1fa01b42850e99 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 18 Aug 2020 17:26:28 +0300 Subject: [PATCH 28/28] Refactor maps services code to be inline with master --- x-pack/plugins/maps/public/kibana_services.ts | 13 +++---------- x-pack/plugins/maps/public/plugin.ts | 4 ---- .../trigger_actions/visualize_geo_field_action.ts | 7 ++++--- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts index 5eef42e386a3e..f8f89ebaed102 100644 --- a/x-pack/plugins/maps/public/kibana_services.ts +++ b/x-pack/plugins/maps/public/kibana_services.ts @@ -9,8 +9,7 @@ import { esFilters } from '../../../../src/plugins/data/public'; import { MapsLegacyConfigType } from '../../../../src/plugins/maps_legacy/public'; import { MapsConfigType } from '../config'; import { MapsPluginStartDependencies } from './plugin'; -import { CoreStart, ApplicationStart } from '../../../../src/core/public'; -import { SharePluginStart } from '../../../../src/plugins/share/public'; +import { CoreStart } from '../../../../src/core/public'; export const SPATIAL_FILTER_TYPE = esFilters.FILTERS.SPATIAL_FILTER; @@ -46,6 +45,7 @@ export const getToasts = () => coreStart.notifications.toasts; export const getSavedObjectsClient = () => coreStart.savedObjects.client; export const getCoreChrome = () => coreStart.chrome; 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 getData = () => pluginsStart.data; @@ -83,11 +83,4 @@ export const getProxyElasticMapsServiceInMaps = () => export const getRegionmapLayers = () => _.get(getKibanaCommonConfig(), 'regionmap.layers', []); export const getTilemap = () => _.get(getKibanaCommonConfig(), 'tilemap', []); -let coreApplication: ApplicationStart; -export const setApplication = (application: ApplicationStart) => (coreApplication = application); -export const getApplication = () => coreApplication; - -let coreShareService: SharePluginStart; -export const setShareService = (shareService: SharePluginStart) => - (coreShareService = shareService); -export const getShareService = () => coreShareService; +export const getShareService = () => pluginsStart.share; diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 93568f9010968..9bb79f2937c68 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -24,8 +24,6 @@ import { setLicenseId, setMapAppConfig, setStartServices, - setApplication, - setShareService, } from './kibana_services'; import { featureCatalogueEntry } from './feature_catalogue_entry'; // @ts-ignore @@ -140,8 +138,6 @@ export class MapsPlugin }); } plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction); - setApplication(core.application); - setShareService(plugins.share); setStartServices(core, plugins); return { 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 aa7225b620ef6..bdeab292b214c 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 @@ -12,10 +12,11 @@ import { VisualizeFieldContext, } from '../../../../../src/plugins/ui_actions/public'; import { - getApplication, + getVisualizeCapabilities, getIndexPatternService, getData, getShareService, + getNavigateToApp, } from '../kibana_services'; import { MAPS_APP_URL_GENERATOR, MapsUrlGeneratorState } from '../url_generator'; import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES, APP_ID, MAP_PATH } from '../../common/constants'; @@ -26,7 +27,7 @@ export const visualizeGeoFieldAction = createAction !!getApplication().capabilities.visualize.show, + isCompatible: async () => !!getVisualizeCapabilities().show, getHref: async (context) => { const url = await getMapsLink(context); return url; @@ -35,7 +36,7 @@ export const visualizeGeoFieldAction = createAction