From ad6c40a313230c750cdc701b319c9f298ce0b124 Mon Sep 17 00:00:00 2001 From: Aaron Caldwell Date: Mon, 14 Jun 2021 17:56:32 -0600 Subject: [PATCH] [Maps] Fix geo alerts handling of multi-fields (#100348) (#102142) --- .../entity_by_expression.test.tsx.snap | 171 ++++++++++++++++++ .../expressions/entity_by_expression.test.tsx | 94 ++++++++++ .../expressions/entity_by_expression.tsx | 19 +- 3 files changed, 277 insertions(+), 7 deletions(-) create mode 100644 x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/__snapshots__/entity_by_expression.test.tsx.snap create mode 100644 x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_by_expression.test.tsx diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/__snapshots__/entity_by_expression.test.tsx.snap b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/__snapshots__/entity_by_expression.test.tsx.snap new file mode 100644 index 00000000000000..d9dd6ec4a0be53 --- /dev/null +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/__snapshots__/entity_by_expression.test.tsx.snap @@ -0,0 +1,171 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render entity by expression with aggregatable field options for entity 1`] = ` + + + + + } + value="FlightNum" + > + + } + closePopover={[Function]} + display="block" + hasArrow={true} + id="popoverForExpression" + isOpen={false} + ownFocus={true} + panelPaddingSize="m" + zIndex={8000} + > +
+
+ + + +
+
+
+
+
+`; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_by_expression.test.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_by_expression.test.tsx new file mode 100644 index 00000000000000..31b89873922c9c --- /dev/null +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_by_expression.test.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mount } from 'enzyme'; +import { EntityByExpression, getValidIndexPatternFields } from './entity_by_expression'; + +const defaultProps = { + errors: { + index: [], + indexId: [], + geoField: [], + entity: [], + dateField: [], + boundaryType: [], + boundaryIndexTitle: [], + boundaryIndexId: [], + boundaryGeoField: [], + name: ['Name is required.'], + interval: [], + alertTypeId: [], + actionConnectors: [], + }, + entity: 'FlightNum', + setAlertParamsEntity: (arg: string) => {}, + indexFields: [ + { + count: 0, + name: 'DestLocation', + type: 'geo_point', + esTypes: ['geo_point'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + count: 0, + name: 'FlightNum', + type: 'string', + esTypes: ['keyword'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + count: 0, + name: 'OriginLocation', + type: 'geo_point', + esTypes: ['geo_point'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + count: 0, + name: 'timestamp', + type: 'date', + esTypes: ['date'], + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + ], + isInvalid: false, +}; + +test('should render entity by expression with aggregatable field options for entity', async () => { + const component = mount(); + expect(component).toMatchSnapshot(); +}); +// + +test('should only use valid index fields', async () => { + // Only the string index field should match + const indexFields = getValidIndexPatternFields(defaultProps.indexFields); + expect(indexFields.length).toEqual(1); + + // Set all agg fields to false, invalidating them for use + const invalidIndexFields = defaultProps.indexFields.map((field) => ({ + ...field, + aggregatable: false, + })); + + const noIndexFields = getValidIndexPatternFields(invalidIndexFields); + expect(noIndexFields.length).toEqual(0); +}); diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_by_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_by_expression.tsx index 2df6439ad56f05..a194bd40d9931f 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_by_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/expressions/entity_by_expression.tsx @@ -22,6 +22,16 @@ interface Props { isInvalid: boolean; } +const ENTITY_TYPES = ['string', 'number', 'ip']; +export function getValidIndexPatternFields(fields: IFieldType[]): IFieldType[] { + return fields.filter((field) => { + const isSpecifiedSupportedField = ENTITY_TYPES.includes(field.type); + const hasLeadingUnderscore = field.name.startsWith('_'); + const isAggregatable = !!field.aggregatable; + return isSpecifiedSupportedField && isAggregatable && !hasLeadingUnderscore; + }); +} + export const EntityByExpression: FunctionComponent = ({ errors, entity, @@ -29,9 +39,6 @@ export const EntityByExpression: FunctionComponent = ({ indexFields, isInvalid, }) => { - // eslint-disable-next-line react-hooks/exhaustive-deps - const ENTITY_TYPES = ['string', 'number', 'ip']; - const usePrevious = (value: T): T | undefined => { const ref = useRef(); useEffect(() => { @@ -48,14 +55,12 @@ export const EntityByExpression: FunctionComponent = ({ }); useEffect(() => { if (!_.isEqual(oldIndexFields, indexFields)) { - fields.current.indexFields = indexFields.filter( - (field: IFieldType) => ENTITY_TYPES.includes(field.type) && !field.name.startsWith('_') - ); + fields.current.indexFields = getValidIndexPatternFields(indexFields); if (!entity && fields.current.indexFields.length) { setAlertParamsEntity(fields.current.indexFields[0].name); } } - }, [ENTITY_TYPES, indexFields, oldIndexFields, setAlertParamsEntity, entity]); + }, [indexFields, oldIndexFields, setAlertParamsEntity, entity]); const indexPopover = (