Commit c77fb9fa by Giordano Ricci Committed by GitHub

Elasticsearch: Fix broken alerting when using pipeline aggregations (#29903)

* Elasticsearch: Fix broken alerting when using pipeline aggregations

* call onRunQuery after every query change
parent 52f79c36
......@@ -12,11 +12,19 @@ const query: ElasticsearchQuery = {
};
describe('ElasticsearchQueryContext', () => {
it('Should call onChange with the default query when the query is empty', () => {
it('Should call onChange and onRunQuery with the default query when the query is empty', () => {
const datasource = { timeField: 'TIMEFIELD' } as ElasticDatasource;
const onChange = jest.fn();
const onRunQuery = jest.fn();
render(<ElasticsearchProvider query={{ refId: 'A' }} onChange={onChange} datasource={datasource} />);
render(
<ElasticsearchProvider
query={{ refId: 'A' }}
onChange={onChange}
datasource={datasource}
onRunQuery={onRunQuery}
/>
);
const changedQuery: ElasticsearchQuery = onChange.mock.calls[0][0];
expect(changedQuery.query).toBeDefined();
......@@ -26,6 +34,8 @@ describe('ElasticsearchQueryContext', () => {
// Should also set timeField to the configured `timeField` option in datasource configuration
expect(changedQuery.timeField).toBe(datasource.timeField);
expect(onRunQuery).toHaveBeenCalled();
});
describe('useQuery Hook', () => {
......@@ -37,7 +47,12 @@ describe('ElasticsearchQueryContext', () => {
it('Should return the current query object', () => {
const wrapper: FunctionComponent = ({ children }) => (
<ElasticsearchProvider datasource={{} as ElasticDatasource} query={query} onChange={() => {}}>
<ElasticsearchProvider
datasource={{} as ElasticDatasource}
query={query}
onChange={() => {}}
onRunQuery={() => {}}
>
{children}
</ElasticsearchProvider>
);
......@@ -61,7 +76,7 @@ describe('ElasticsearchQueryContext', () => {
const datasource = {} as ElasticDatasource;
const wrapper: FunctionComponent = ({ children }) => (
<ElasticsearchProvider datasource={datasource} query={query} onChange={() => {}}>
<ElasticsearchProvider datasource={datasource} query={query} onChange={() => {}} onRunQuery={() => {}}>
{children}
</ElasticsearchProvider>
);
......
import React, { createContext, FunctionComponent, useContext } from 'react';
import React, { createContext, FunctionComponent, useCallback, useContext } from 'react';
import { ElasticDatasource } from '../../datasource';
import { combineReducers, useStatelessReducer, DispatchContext } from '../../hooks/useStatelessReducer';
import { ElasticsearchQuery } from '../../types';
......@@ -13,10 +13,25 @@ const QueryContext = createContext<ElasticsearchQuery | undefined>(undefined);
interface Props {
query: ElasticsearchQuery;
onChange: (query: ElasticsearchQuery) => void;
onRunQuery: () => void;
datasource: ElasticDatasource;
}
export const ElasticsearchProvider: FunctionComponent<Props> = ({ children, onChange, query, datasource }) => {
export const ElasticsearchProvider: FunctionComponent<Props> = ({
children,
onChange,
onRunQuery,
query,
datasource,
}) => {
const onStateChange = useCallback(
(query: ElasticsearchQuery) => {
onChange(query);
onRunQuery();
},
[onChange, onRunQuery]
);
const reducer = combineReducers({
query: queryReducer,
alias: aliasPatternReducer,
......@@ -26,7 +41,7 @@ export const ElasticsearchProvider: FunctionComponent<Props> = ({ children, onCh
const dispatch = useStatelessReducer(
// timeField is part of the query model, but its value is always set to be the one from datasource settings.
newState => onChange({ ...query, ...newState, timeField: datasource.timeField }),
newState => onStateChange({ ...query, ...newState, timeField: datasource.timeField }),
query,
reducer
);
......
......@@ -150,6 +150,7 @@ interface Logs extends BaseMetricAggregation {
export interface BasePipelineMetricAggregation extends MetricAggregationWithField {
type: PipelineMetricAggregationType;
pipelineAgg?: string;
}
interface PipelineMetricAggregationWithMultipleBucketPaths extends BaseMetricAggregation {
......
......@@ -106,7 +106,7 @@ describe('Metric Aggregations Reducer', () => {
it("Should correctly change aggregation's field", () => {
const firstAggregation: MetricAggregation = {
id: '1',
type: 'count',
type: 'min',
};
const secondAggregation: MetricAggregation = {
id: '2',
......@@ -116,12 +116,22 @@ describe('Metric Aggregations Reducer', () => {
const expectedSecondAggregation = {
...secondAggregation,
field: 'new field',
pipelineAgg: 'new field',
};
const expectedFirstAggregation = {
...firstAggregation,
field: 'new field',
};
reducerTester()
.givenReducer(reducer, [firstAggregation, secondAggregation])
// When changing a a pipelineAggregation field we set both pipelineAgg and field
.whenActionIsDispatched(changeMetricField(secondAggregation.id, expectedSecondAggregation.field))
.thenStateShouldEqual([firstAggregation, expectedSecondAggregation]);
.thenStateShouldEqual([firstAggregation, expectedSecondAggregation])
// otherwhise only field
.whenActionIsDispatched(changeMetricField(firstAggregation.id, expectedFirstAggregation.field))
.thenStateShouldEqual([expectedFirstAggregation, expectedSecondAggregation]);
});
it('Should correctly toggle `hide` field', () => {
......
......@@ -2,7 +2,12 @@ import { defaultMetricAgg } from '../../../../query_def';
import { ElasticsearchQuery } from '../../../../types';
import { removeEmpty } from '../../../../utils';
import { INIT, InitAction } from '../../state';
import { isMetricAggregationWithMeta, isMetricAggregationWithSettings, MetricAggregation } from '../aggregations';
import {
isMetricAggregationWithMeta,
isMetricAggregationWithSettings,
isPipelineAggregation,
MetricAggregation,
} from '../aggregations';
import { getChildren, metricAggregationConfig } from '../utils';
import {
ADD_METRIC,
......@@ -64,10 +69,16 @@ export const reducer = (
return metric;
}
return {
const newMetric = {
...metric,
field: action.payload.field,
};
if (isPipelineAggregation(metric)) {
return { ...newMetric, pipelineAgg: action.payload.field };
}
return newMetric;
});
case TOGGLE_METRIC_VISIBILITY:
......
......@@ -12,8 +12,13 @@ import { useNextId } from '../../hooks/useNextId';
export type ElasticQueryEditorProps = QueryEditorProps<ElasticDatasource, ElasticsearchQuery, ElasticsearchOptions>;
export const QueryEditor: FunctionComponent<ElasticQueryEditorProps> = ({ query, onChange, datasource }) => (
<ElasticsearchProvider datasource={datasource} onChange={onChange} query={query}>
export const QueryEditor: FunctionComponent<ElasticQueryEditorProps> = ({
query,
onChange,
onRunQuery,
datasource,
}) => (
<ElasticsearchProvider datasource={datasource} onChange={onChange} onRunQuery={onRunQuery} query={query}>
<QueryEditorForm value={query} />
</ElasticsearchProvider>
);
......
......@@ -13,7 +13,7 @@ describe('useNextId', () => {
};
const wrapper: FunctionComponent = ({ children }) => {
return (
<ElasticsearchProvider query={query} datasource={{} as any} onChange={() => {}}>
<ElasticsearchProvider query={query} datasource={{} as any} onChange={() => {}} onRunQuery={() => {}}>
{children}
</ElasticsearchProvider>
);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment