From 3dbe9dcad25c0946ccd9e5b6f22936462dd43b6a Mon Sep 17 00:00:00 2001 From: Garrett Spong Date: Mon, 18 May 2020 20:25:57 -0600 Subject: [PATCH] [SIEM] [Maps] Fixes Network Map empty tooltip (#66828) (#66945) ## Summary Resolves https://github.com/elastic/kibana/issues/63474, and expands `ITooltipProperty`'s `rawValue` type to include `string[]` as mentioned [here](https://github.com/elastic/kibana/pull/61264#discussion_r398858559). ![image](https://user-images.githubusercontent.com/2946766/82100568-2c0e1480-96c7-11ea-958e-5b1c6b6a3db9.png) ### Checklist Delete any items that are not applicable to this PR. - [X] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios --- .../public/classes/fields/es_agg_field.ts | 2 +- .../public/classes/fields/es_doc_field.ts | 2 +- .../maps/public/classes/fields/field.ts | 4 +- .../fields/top_term_percentage_field.ts | 2 +- .../classes/tooltips/es_tooltip_property.ts | 9 +++- .../classes/tooltips/join_tooltip_property.ts | 2 +- .../classes/tooltips/tooltip_property.ts | 10 ++-- x-pack/plugins/maps/public/index.ts | 1 + .../point_tool_tip_content.test.tsx.snap | 3 +- .../line_tool_tip_content.test.tsx | 27 ++++------- .../map_tool_tip/line_tool_tip_content.tsx | 19 ++++---- .../embeddables/map_tool_tip/map_tool_tip.tsx | 7 +-- .../point_tool_tip_content.test.tsx | 46 ++----------------- .../map_tool_tip/point_tool_tip_content.tsx | 27 +++++------ .../network/components/embeddables/types.ts | 10 ---- 15 files changed, 59 insertions(+), 112 deletions(-) diff --git a/x-pack/plugins/maps/public/classes/fields/es_agg_field.ts b/x-pack/plugins/maps/public/classes/fields/es_agg_field.ts index 4a3ac6390c5a7..60d437d2321b5 100644 --- a/x-pack/plugins/maps/public/classes/fields/es_agg_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/es_agg_field.ts @@ -87,7 +87,7 @@ export class ESAggField implements IESAggField { return this._esDocField ? this._esDocField.getName() : ''; } - async createTooltipProperty(value: string | undefined): Promise { + async createTooltipProperty(value: string | string[] | undefined): Promise { const indexPattern = await this._source.getIndexPattern(); const tooltipProperty = new TooltipProperty(this.getName(), await this.getLabel(), value); return new ESAggTooltipProperty(tooltipProperty, indexPattern, this, this.getAggType()); diff --git a/x-pack/plugins/maps/public/classes/fields/es_doc_field.ts b/x-pack/plugins/maps/public/classes/fields/es_doc_field.ts index 670b3ba32888b..9faa33fae5a43 100644 --- a/x-pack/plugins/maps/public/classes/fields/es_doc_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/es_doc_field.ts @@ -45,7 +45,7 @@ export class ESDocField extends AbstractField implements IField { : indexPatternField; } - async createTooltipProperty(value: string | undefined): Promise { + async createTooltipProperty(value: string | string[] | undefined): Promise { const indexPattern = await this._source.getIndexPattern(); const tooltipProperty = new TooltipProperty(this.getName(), await this.getLabel(), value); return new ESTooltipProperty(tooltipProperty, indexPattern, this as IField); diff --git a/x-pack/plugins/maps/public/classes/fields/field.ts b/x-pack/plugins/maps/public/classes/fields/field.ts index 04daedc59c032..dfd5dc05f7b83 100644 --- a/x-pack/plugins/maps/public/classes/fields/field.ts +++ b/x-pack/plugins/maps/public/classes/fields/field.ts @@ -14,7 +14,7 @@ export interface IField { canValueBeFormatted(): boolean; getLabel(): Promise; getDataType(): Promise; - createTooltipProperty(value: string | undefined): Promise; + createTooltipProperty(value: string | string[] | undefined): Promise; getSource(): IVectorSource; getOrigin(): FIELD_ORIGIN; isValid(): boolean; @@ -60,7 +60,7 @@ export class AbstractField implements IField { return this._fieldName; } - async createTooltipProperty(value: string | undefined): Promise { + async createTooltipProperty(value: string | string[] | undefined): Promise { const label = await this.getLabel(); return new TooltipProperty(this.getName(), label, value); } diff --git a/x-pack/plugins/maps/public/classes/fields/top_term_percentage_field.ts b/x-pack/plugins/maps/public/classes/fields/top_term_percentage_field.ts index 84bade4d94490..6c504daf3e192 100644 --- a/x-pack/plugins/maps/public/classes/fields/top_term_percentage_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/top_term_percentage_field.ts @@ -48,7 +48,7 @@ export class TopTermPercentageField implements IESAggField { return 'number'; } - async createTooltipProperty(value: string | undefined): Promise { + async createTooltipProperty(value: string | string[] | undefined): Promise { return new TooltipProperty(this.getName(), await this.getLabel(), value); } diff --git a/x-pack/plugins/maps/public/classes/tooltips/es_tooltip_property.ts b/x-pack/plugins/maps/public/classes/tooltips/es_tooltip_property.ts index d2fdcfaab476c..516ad25f933f6 100644 --- a/x-pack/plugins/maps/public/classes/tooltips/es_tooltip_property.ts +++ b/x-pack/plugins/maps/public/classes/tooltips/es_tooltip_property.ts @@ -33,7 +33,7 @@ export class ESTooltipProperty implements ITooltipProperty { return this._tooltipProperty.getPropertyName(); } - getRawValue(): string | undefined { + getRawValue(): string | string[] | undefined { return this._tooltipProperty.getRawValue(); } @@ -48,7 +48,12 @@ export class ESTooltipProperty implements ITooltipProperty { const indexPatternField = this._getIndexPatternField(); if (!indexPatternField || !this._field.canValueBeFormatted()) { - return _.escape(this.getRawValue()); + const rawValue = this.getRawValue(); + if (Array.isArray(rawValue)) { + return _.escape(rawValue.join()); + } else { + return _.escape(rawValue); + } } const htmlConverter = indexPatternField.format.getConverterFor('html'); diff --git a/x-pack/plugins/maps/public/classes/tooltips/join_tooltip_property.ts b/x-pack/plugins/maps/public/classes/tooltips/join_tooltip_property.ts index cc95c12ef630f..fad6d6b84f77f 100644 --- a/x-pack/plugins/maps/public/classes/tooltips/join_tooltip_property.ts +++ b/x-pack/plugins/maps/public/classes/tooltips/join_tooltip_property.ts @@ -29,7 +29,7 @@ export class JoinTooltipProperty implements ITooltipProperty { return this._tooltipProperty.getPropertyName(); } - getRawValue(): string | undefined { + getRawValue(): string | string[] | undefined { return this._tooltipProperty.getRawValue(); } diff --git a/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts b/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts index 8da2ed795943b..7149fe29f90ec 100644 --- a/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts +++ b/x-pack/plugins/maps/public/classes/tooltips/tooltip_property.ts @@ -12,7 +12,7 @@ export interface ITooltipProperty { getPropertyKey(): string; getPropertyName(): string; getHtmlDisplayValue(): string; - getRawValue(): string | undefined; + getRawValue(): string | string[] | undefined; isFilterable(): boolean; getESFilters(): Promise; } @@ -41,10 +41,10 @@ export type RenderToolTipContent = (params: RenderTooltipContentParams) => JSX.E export class TooltipProperty implements ITooltipProperty { private readonly _propertyKey: string; - private readonly _rawValue: string | undefined; + private readonly _rawValue: string | string[] | undefined; private readonly _propertyName: string; - constructor(propertyKey: string, propertyName: string, rawValue: string | undefined) { + constructor(propertyKey: string, propertyName: string, rawValue: string | string[] | undefined) { this._propertyKey = propertyKey; this._propertyName = propertyName; this._rawValue = rawValue; @@ -59,10 +59,10 @@ export class TooltipProperty implements ITooltipProperty { } getHtmlDisplayValue(): string { - return _.escape(this._rawValue); + return _.escape(Array.isArray(this._rawValue) ? this._rawValue.join() : this._rawValue); } - getRawValue(): string | undefined { + getRawValue(): string | string[] | undefined { return this._rawValue; } diff --git a/x-pack/plugins/maps/public/index.ts b/x-pack/plugins/maps/public/index.ts index e3feb47691877..79953e35d51e5 100644 --- a/x-pack/plugins/maps/public/index.ts +++ b/x-pack/plugins/maps/public/index.ts @@ -12,3 +12,4 @@ export const plugin: PluginInitializer = () => }; export { MAP_SAVED_OBJECT_TYPE } from '../common/constants'; +export { ITooltipProperty } from './classes/tooltips/tooltip_property'; diff --git a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap index 9d39b6e59365f..8927e492993d0 100644 --- a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap +++ b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/__snapshots__/point_tool_tip_content.test.tsx.snap @@ -6,8 +6,9 @@ exports[`PointToolTipContent renders correctly against snapshot 1`] = ` contextId="contextId" featureProps={ Array [ - Object { + TooltipProperty { "_propertyKey": "host.name", + "_propertyName": "host.name", "_rawValue": "testPropValue", }, ] diff --git a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/line_tool_tip_content.test.tsx b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/line_tool_tip_content.test.tsx index a0e57a2e850c1..aef41ddaef5ae 100644 --- a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/line_tool_tip_content.test.tsx +++ b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/line_tool_tip_content.test.tsx @@ -7,35 +7,24 @@ import { shallow } from 'enzyme'; import React from 'react'; import { LineToolTipContentComponent } from './line_tool_tip_content'; -import { FeatureProperty } from '../types'; import { SUM_OF_CLIENT_BYTES, SUM_OF_DESTINATION_BYTES, SUM_OF_SERVER_BYTES, SUM_OF_SOURCE_BYTES, } from '../map_config'; +import { ITooltipProperty } from '../../../../../../maps/public'; +import { TooltipProperty } from '../../../../../../maps/public/classes/tooltips/tooltip_property'; describe('LineToolTipContent', () => { - const mockFeatureProps: FeatureProperty[] = [ - { - _propertyKey: SUM_OF_DESTINATION_BYTES, - _rawValue: 'testPropValue', - }, - { - _propertyKey: SUM_OF_SOURCE_BYTES, - _rawValue: 'testPropValue', - }, + const mockFeatureProps: ITooltipProperty[] = [ + new TooltipProperty(SUM_OF_DESTINATION_BYTES, SUM_OF_DESTINATION_BYTES, 'testPropValue'), + new TooltipProperty(SUM_OF_SOURCE_BYTES, SUM_OF_SOURCE_BYTES, 'testPropValue'), ]; - const mockClientServerFeatureProps: FeatureProperty[] = [ - { - _propertyKey: SUM_OF_SERVER_BYTES, - _rawValue: 'testPropValue', - }, - { - _propertyKey: SUM_OF_CLIENT_BYTES, - _rawValue: 'testPropValue', - }, + const mockClientServerFeatureProps: ITooltipProperty[] = [ + new TooltipProperty(SUM_OF_SERVER_BYTES, SUM_OF_SERVER_BYTES, 'testPropValue'), + new TooltipProperty(SUM_OF_CLIENT_BYTES, SUM_OF_CLIENT_BYTES, 'testPropValue'), ]; test('renders correctly against snapshot', () => { diff --git a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/line_tool_tip_content.tsx b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/line_tool_tip_content.tsx index 7c2d5e51d813f..9238f7ec65a20 100644 --- a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/line_tool_tip_content.tsx +++ b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/line_tool_tip_content.tsx @@ -14,8 +14,9 @@ import { SUM_OF_SERVER_BYTES, SUM_OF_SOURCE_BYTES, } from '../map_config'; -import { FeatureProperty } from '../types'; + import * as i18n from '../translations'; +import { ITooltipProperty } from '../../../../../../maps/public'; const FlowBadge = (styled(EuiBadge)` height: 45px; @@ -28,20 +29,22 @@ const EuiFlexGroupStyled = styled(EuiFlexGroup)` interface LineToolTipContentProps { contextId: string; - featureProps: FeatureProperty[]; + featureProps: ITooltipProperty[]; } export const LineToolTipContentComponent = ({ contextId, featureProps, }: LineToolTipContentProps) => { - const lineProps = featureProps.reduce>( - (acc, f) => ({ + const lineProps = featureProps.reduce>((acc, f) => { + const rawValue = f.getRawValue() ?? []; + return { ...acc, - ...{ [f._propertyKey]: Array.isArray(f._rawValue) ? f._rawValue : [f._rawValue] }, - }), - {} - ); + ...{ + [f.getPropertyKey()]: Array.isArray(rawValue) ? rawValue : [rawValue], + }, + }; + }, {}); const isSrcDest = Object.keys(lineProps).includes(SUM_OF_SOURCE_BYTES); diff --git a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/map_tool_tip.tsx b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/map_tool_tip.tsx index 0f38c350986b4..10267d398848e 100644 --- a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/map_tool_tip.tsx +++ b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/map_tool_tip.tsx @@ -11,12 +11,13 @@ import { EuiLoadingSpinner, EuiOutsideClickDetector, } from '@elastic/eui'; -import { FeatureGeometry, FeatureProperty, MapToolTipProps } from '../types'; +import { FeatureGeometry, MapToolTipProps } from '../types'; import { ToolTipFooter } from './tooltip_footer'; import { LineToolTipContent } from './line_tool_tip_content'; import { PointToolTipContent } from './point_tool_tip_content'; import { Loader } from '../../../../common/components/loader'; import * as i18n from '../translations'; +import { ITooltipProperty } from '../../../../../../maps/public'; export const MapToolTipComponent = ({ addFilters, @@ -31,7 +32,7 @@ export const MapToolTipComponent = ({ const [isLoadingNextFeature, setIsLoadingNextFeature] = useState(false); const [isError, setIsError] = useState(false); const [featureIndex, setFeatureIndex] = useState(0); - const [featureProps, setFeatureProps] = useState([]); + const [featureProps, setFeatureProps] = useState([]); const [featureGeometry, setFeatureGeometry] = useState(null); const [, setLayerName] = useState(''); @@ -64,7 +65,7 @@ export const MapToolTipComponent = ({ getLayerName(layerId), ]); - setFeatureProps((featureProperties as unknown) as FeatureProperty[]); + setFeatureProps(featureProperties); setFeatureGeometry(featureGeo); setLayerName(layerNameString); } catch (e) { diff --git a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx index d5a7c51ccdeb8..36b9f44e19630 100644 --- a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx +++ b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx @@ -6,29 +6,17 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { FeatureProperty } from '../types'; import { getRenderedFieldValue, PointToolTipContentComponent } from './point_tool_tip_content'; import { TestProviders } from '../../../../common/mock'; import { getEmptyStringTag } from '../../../../common/components/empty_value'; import { HostDetailsLink, IPDetailsLink } from '../../../../common/components/links'; -import { useMountAppended } from '../../../../common/utils/use_mount_appended'; import { FlowTarget } from '../../../../graphql/types'; +import { ITooltipProperty } from '../../../../../../maps/public'; +import { TooltipProperty } from '../../../../../../maps/public/classes/tooltips/tooltip_property'; describe('PointToolTipContent', () => { - const mount = useMountAppended(); - - const mockFeatureProps: FeatureProperty[] = [ - { - _propertyKey: 'host.name', - _rawValue: 'testPropValue', - }, - ]; - - const mockFeaturePropsArrayValue: FeatureProperty[] = [ - { - _propertyKey: 'host.name', - _rawValue: ['testPropValue1', 'testPropValue2'], - }, + const mockFeatureProps: ITooltipProperty[] = [ + new TooltipProperty('host.name', 'host.name', 'testPropValue'), ]; test('renders correctly against snapshot', () => { @@ -46,32 +34,6 @@ describe('PointToolTipContent', () => { expect(wrapper.find('PointToolTipContentComponent')).toMatchSnapshot(); }); - test('renders array filter correctly', () => { - const closeTooltip = jest.fn(); - - const wrapper = mount( - - - - ); - expect(wrapper.find('[data-test-subj="add-to-kql-host.name"]').prop('filter')).toEqual({ - meta: { - alias: null, - disabled: false, - key: 'host.name', - negate: false, - params: { query: 'testPropValue1' }, - type: 'phrase', - value: 'testPropValue1', - }, - query: { match: { 'host.name': { query: 'testPropValue1', type: 'phrase' } } }, - }); - }); - describe('#getRenderedFieldValue', () => { test('it returns empty tag if value is empty', () => { expect(getRenderedFieldValue('host.name', '')).toStrictEqual(getEmptyStringTag()); diff --git a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.tsx b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.tsx index c691407f6166e..16494c01ac280 100644 --- a/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.tsx +++ b/x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.tsx @@ -6,23 +6,19 @@ import React from 'react'; import { sourceDestinationFieldMappings } from '../map_config'; -import { - AddFilterToGlobalSearchBar, - createFilter, -} from '../../../../common/components/add_filter_to_global_search_bar'; import { getEmptyTagValue, getOrEmptyTagFromValue, } from '../../../../common/components/empty_value'; import { DescriptionListStyled } from '../../../../common/components/page'; -import { FeatureProperty } from '../types'; import { HostDetailsLink, IPDetailsLink } from '../../../../common/components/links'; import { DefaultFieldRenderer } from '../../../../timelines/components/field_renderers/field_renderers'; import { FlowTarget } from '../../../../graphql/types'; +import { ITooltipProperty } from '../../../../../../maps/public'; interface PointToolTipContentProps { contextId: string; - featureProps: FeatureProperty[]; + featureProps: ITooltipProperty[]; closeTooltip?(): void; } @@ -31,15 +27,14 @@ export const PointToolTipContentComponent = ({ featureProps, closeTooltip, }: PointToolTipContentProps) => { - const featureDescriptionListItems = featureProps.map( - ({ _propertyKey: key, _rawValue: value }) => ({ + const featureDescriptionListItems = featureProps.map(featureProp => { + const key = featureProp.getPropertyKey(); + const value = featureProp.getRawValue() ?? []; + + return { title: sourceDestinationFieldMappings[key], description: ( - + <> {value != null ? ( + ), - }) - ); + }; + }); return ; }; diff --git a/x-pack/plugins/siem/public/network/components/embeddables/types.ts b/x-pack/plugins/siem/public/network/components/embeddables/types.ts index e111c2728ba7e..9a49046634c3b 100644 --- a/x-pack/plugins/siem/public/network/components/embeddables/types.ts +++ b/x-pack/plugins/siem/public/network/components/embeddables/types.ts @@ -40,16 +40,6 @@ export interface MapFeature { layerId: string; } -export interface LoadFeatureProps { - layerId: string; - featureId: number; -} - -export interface FeatureProperty { - _propertyKey: string; - _rawValue: string | string[]; -} - export interface FeatureGeometry { coordinates: [number]; type: string;