mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Maps] Fix geo alerts handling of multi-fields (#100348)
This commit is contained in:
parent
7cee2eefdc
commit
6e0aed79c3
3 changed files with 277 additions and 7 deletions
|
@ -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>
|
||||
`;
|
|
@ -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);
|
||||
});
|
|
@ -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}>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue