Skip to content

Commit

Permalink
[SIEM] [Maps] Fixes Network Map empty tooltip (elastic#66828)
Browse files Browse the repository at this point in the history
## Summary

Resolves elastic#63474, and expands `ITooltipProperty`'s `rawValue` type to include `string[]` as mentioned [here](elastic#61264 (comment)).

![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

# Conflicts:
#	x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/map_tool_tip.tsx
#	x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx
#	x-pack/plugins/siem/public/network/components/embeddables/map_tool_tip/point_tool_tip_content.tsx
#	x-pack/plugins/siem/public/network/components/embeddables/types.ts
  • Loading branch information
spong committed May 19, 2020
1 parent 17e7261 commit 11e6e0d
Show file tree
Hide file tree
Showing 15 changed files with 61 additions and 112 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/maps/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export const plugin: PluginInitializer<MapsPluginSetup, MapsPluginStart> = () =>
};

export { MAP_SAVED_OBJECT_TYPE } from '../common/constants';
export { ITooltipProperty } from './layers/tooltips/tooltip_property';
2 changes: 1 addition & 1 deletion x-pack/plugins/maps/public/layers/fields/es_agg_field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export class ESAggField implements IESAggField {
return this._esDocField ? this._esDocField.getName() : '';
}

async createTooltipProperty(value: string | undefined): Promise<ITooltipProperty> {
async createTooltipProperty(value: string | string[] | undefined): Promise<ITooltipProperty> {
const indexPattern = await this._source.getIndexPattern();
const tooltipProperty = new TooltipProperty(this.getName(), await this.getLabel(), value);
return new ESAggTooltipProperty(tooltipProperty, indexPattern, this, this.getAggType());
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/maps/public/layers/fields/es_doc_field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class ESDocField extends AbstractField implements IField {
: indexPatternField;
}

async createTooltipProperty(value: string | undefined): Promise<ITooltipProperty> {
async createTooltipProperty(value: string | string[] | undefined): Promise<ITooltipProperty> {
const indexPattern = await this._source.getIndexPattern();
const tooltipProperty = new TooltipProperty(this.getName(), await this.getLabel(), value);
return new ESTooltipProperty(tooltipProperty, indexPattern, this as IField);
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/maps/public/layers/fields/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface IField {
canValueBeFormatted(): boolean;
getLabel(): Promise<string>;
getDataType(): Promise<string>;
createTooltipProperty(value: string | undefined): Promise<ITooltipProperty>;
createTooltipProperty(value: string | string[] | undefined): Promise<ITooltipProperty>;
getSource(): IVectorSource;
getOrigin(): FIELD_ORIGIN;
isValid(): boolean;
Expand Down Expand Up @@ -60,7 +60,7 @@ export class AbstractField implements IField {
return this._fieldName;
}

async createTooltipProperty(value: string | undefined): Promise<ITooltipProperty> {
async createTooltipProperty(value: string | string[] | undefined): Promise<ITooltipProperty> {
const label = await this.getLabel();
return new TooltipProperty(this.getName(), label, value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class TopTermPercentageField implements IESAggField {
return 'number';
}

async createTooltipProperty(value: string | undefined): Promise<ITooltipProperty> {
async createTooltipProperty(value: string | string[] | undefined): Promise<ITooltipProperty> {
return new TooltipProperty(this.getName(), await this.getLabel(), value);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class ESTooltipProperty implements ITooltipProperty {
return this._tooltipProperty.getPropertyName();
}

getRawValue(): string | undefined {
getRawValue(): string | string[] | undefined {
return this._tooltipProperty.getRawValue();
}

Expand All @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class JoinTooltipProperty implements ITooltipProperty {
return this._tooltipProperty.getPropertyName();
}

getRawValue(): string | undefined {
getRawValue(): string | string[] | undefined {
return this._tooltipProperty.getRawValue();
}

Expand Down
10 changes: 5 additions & 5 deletions x-pack/plugins/maps/public/layers/tooltips/tooltip_property.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface ITooltipProperty {
getPropertyKey(): string;
getPropertyName(): string;
getHtmlDisplayValue(): string;
getRawValue(): string | undefined;
getRawValue(): string | string[] | undefined;
isFilterable(): boolean;
getESFilters(): Promise<Filter[]>;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Record<string, string[]>>(
(acc, f) => ({
const lineProps = featureProps.reduce<Record<string, string[]>>((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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 '../../loader';
import * as i18n from '../translations';
import { ITooltipProperty } from '../../../../../maps/public';

export const MapToolTipComponent = ({
addFilters,
Expand All @@ -31,7 +32,7 @@ export const MapToolTipComponent = ({
const [isLoadingNextFeature, setIsLoadingNextFeature] = useState<boolean>(false);
const [isError, setIsError] = useState<boolean>(false);
const [featureIndex, setFeatureIndex] = useState<number>(0);
const [featureProps, setFeatureProps] = useState<FeatureProperty[]>([]);
const [featureProps, setFeatureProps] = useState<ITooltipProperty[]>([]);
const [featureGeometry, setFeatureGeometry] = useState<FeatureGeometry | null>(null);
const [, setLayerName] = useState<string>('');

Expand Down Expand Up @@ -64,7 +65,7 @@ export const MapToolTipComponent = ({
getLayerName(layerId),
]);

setFeatureProps((featureProperties as unknown) as FeatureProperty[]);
setFeatureProps(featureProperties);
setFeatureGeometry(featureGeo);
setLayerName(layerNameString);
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,19 @@

import { shallow } from 'enzyme';
import React from 'react';
import { FeatureProperty } from '../types';
import { getRenderedFieldValue, PointToolTipContentComponent } from './point_tool_tip_content';
import { TestProviders } from '../../../mock';
import { getEmptyStringTag } from '../../empty_value';
import { HostDetailsLink, IPDetailsLink } from '../../links';
import { useMountAppended } from '../../../utils/use_mount_appended';
import { FlowTarget } from '../../../graphql/types';
import {
ITooltipProperty,
TooltipProperty,
} from '../../../../../maps/public/layers/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', () => {
Expand All @@ -46,32 +36,6 @@ describe('PointToolTipContent', () => {
expect(wrapper.find('PointToolTipContentComponent')).toMatchSnapshot();
});

test('renders array filter correctly', () => {
const closeTooltip = jest.fn();

const wrapper = mount(
<TestProviders>
<PointToolTipContentComponent
contextId={'contextId'}
featureProps={mockFeaturePropsArrayValue}
closeTooltip={closeTooltip}
/>
</TestProviders>
);
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,16 @@

import React from 'react';
import { sourceDestinationFieldMappings } from '../map_config';
import {
AddFilterToGlobalSearchBar,
createFilter,
} from '../../page/add_filter_to_global_search_bar';
import { getEmptyTagValue, getOrEmptyTagFromValue } from '../../empty_value';
import { DescriptionListStyled } from '../../page';
import { FeatureProperty } from '../types';
import { HostDetailsLink, IPDetailsLink } from '../../links';
import { DefaultFieldRenderer } from '../../field_renderers/field_renderers';
import { FlowTarget } from '../../../graphql/types';
import { ITooltipProperty } from '../../../../../maps/public';

interface PointToolTipContentProps {
contextId: string;
featureProps: FeatureProperty[];
featureProps: ITooltipProperty[];
closeTooltip?(): void;
}

Expand All @@ -28,15 +24,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: (
<AddFilterToGlobalSearchBar
filter={createFilter(key, Array.isArray(value) ? value[0] : value)}
onFilterAdded={closeTooltip}
data-test-subj={`add-to-kql-${key}`}
>
<>
{value != null ? (
<DefaultFieldRenderer
rowItems={Array.isArray(value) ? value : [value]}
Expand All @@ -47,10 +42,10 @@ export const PointToolTipContentComponent = ({
) : (
getEmptyTagValue()
)}
</AddFilterToGlobalSearchBar>
</>
),
})
);
};
});

return <DescriptionListStyled listItems={featureDescriptionListItems} />;
};
Expand Down
10 changes: 0 additions & 10 deletions x-pack/plugins/siem/public/components/embeddables/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 11e6e0d

Please sign in to comment.