[Maps] Fix geo alerts handling of multi-fields (#100348)

This commit is contained in:
Aaron Caldwell 2021-06-14 15:51:33 -06:00 committed by GitHub
parent 7cee2eefdc
commit 6e0aed79c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 277 additions and 7 deletions

View file

@ -0,0 +1,171 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render entity by expression with aggregatable field options for entity 1`] = `
<EntityByExpression
entity="FlightNum"
errors={
Object {
"actionConnectors": Array [],
"alertTypeId": Array [],
"boundaryGeoField": Array [],
"boundaryIndexId": Array [],
"boundaryIndexTitle": Array [],
"boundaryType": Array [],
"dateField": Array [],
"entity": Array [],
"geoField": Array [],
"index": Array [],
"indexId": Array [],
"interval": Array [],
"name": Array [
"Name is required.",
],
}
}
indexFields={
Array [
Object {
"aggregatable": true,
"count": 0,
"esTypes": Array [
"geo_point",
],
"name": "DestLocation",
"readFromDocValues": true,
"scripted": false,
"searchable": true,
"type": "geo_point",
},
Object {
"aggregatable": true,
"count": 0,
"esTypes": Array [
"keyword",
],
"name": "FlightNum",
"readFromDocValues": true,
"scripted": false,
"searchable": true,
"type": "string",
},
Object {
"aggregatable": true,
"count": 0,
"esTypes": Array [
"geo_point",
],
"name": "OriginLocation",
"readFromDocValues": true,
"scripted": false,
"searchable": true,
"type": "geo_point",
},
Object {
"aggregatable": true,
"count": 0,
"esTypes": Array [
"date",
],
"name": "timestamp",
"readFromDocValues": true,
"scripted": false,
"searchable": true,
"type": "date",
},
]
}
isInvalid={false}
setAlertParamsEntity={[Function]}
>
<ExpressionWithPopover
defaultValue="Select entity field"
expressionDescription="by"
isInvalid={false}
popoverContent={
<EuiFormRow
describedByIds={Array []}
display="row"
error={Array []}
fullWidth={true}
hasChildLabel={true}
hasEmptyLabelSpace={false}
id="entitySelect"
labelType="label"
>
<SingleFieldSelect
fields={Array []}
onChange={[Function]}
placeholder="Select entity field"
value="FlightNum"
/>
</EuiFormRow>
}
value="FlightNum"
>
<EuiPopover
anchorPosition="downLeft"
button={
<EuiExpression
data-test-subj="selectIndexExpression"
description="by"
display="columns"
isActive={false}
isInvalid={false}
onClick={[Function]}
value="FlightNum"
/>
}
closePopover={[Function]}
display="block"
hasArrow={true}
id="popoverForExpression"
isOpen={false}
ownFocus={true}
panelPaddingSize="m"
zIndex={8000}
>
<div
className="euiPopover euiPopover--anchorDownLeft euiPopover--displayBlock"
id="popoverForExpression"
>
<div
className="euiPopover__anchor"
>
<EuiExpression
data-test-subj="selectIndexExpression"
description="by"
display="columns"
isActive={false}
isInvalid={false}
onClick={[Function]}
value="FlightNum"
>
<button
className="euiExpression euiExpression-isClickable euiExpression-isUppercase euiExpression--columns euiExpression--secondary"
data-test-subj="selectIndexExpression"
onClick={[Function]}
>
<span
className="euiExpression__description"
style={
Object {
"flexBasis": "20%",
}
}
>
by
</span>
<span
className="euiExpression__value"
>
FlightNum
</span>
</button>
</EuiExpression>
</div>
</div>
</EuiPopover>
</ExpressionWithPopover>
</EntityByExpression>
`;

View file

@ -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(<EntityByExpression {...defaultProps} />);
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);
});

View file

@ -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<Props> = ({
errors,
entity,
@ -29,9 +39,6 @@ export const EntityByExpression: FunctionComponent<Props> = ({
indexFields,
isInvalid,
}) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
const ENTITY_TYPES = ['string', 'number', 'ip'];
const usePrevious = <T extends unknown>(value: T): T | undefined => {
const ref = useRef<T>();
useEffect(() => {
@ -48,14 +55,12 @@ export const EntityByExpression: FunctionComponent<Props> = ({
});
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 = (
<EuiFormRow id="entitySelect" fullWidth error={errors.index}>