diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.test.tsx b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.test.tsx index 5f04a1e7ead..d40bdf7d2c9 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.test.tsx +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.test.tsx @@ -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(); + render( + + ); 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 }) => ( - {}}> + {}} + onRunQuery={() => {}} + > {children} ); @@ -61,7 +76,7 @@ describe('ElasticsearchQueryContext', () => { const datasource = {} as ElasticDatasource; const wrapper: FunctionComponent = ({ children }) => ( - {}}> + {}} onRunQuery={() => {}}> {children} ); diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.tsx b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.tsx index 8fb94b487ff..d28cf8a9675 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.tsx +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.tsx @@ -1,4 +1,4 @@ -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(undefined); interface Props { query: ElasticsearchQuery; onChange: (query: ElasticsearchQuery) => void; + onRunQuery: () => void; datasource: ElasticDatasource; } -export const ElasticsearchProvider: FunctionComponent = ({ children, onChange, query, datasource }) => { +export const ElasticsearchProvider: FunctionComponent = ({ + 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 = ({ 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 ); diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/aggregations.ts b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/aggregations.ts index fe6e645d2a3..032696b2828 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/aggregations.ts +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/aggregations.ts @@ -150,6 +150,7 @@ interface Logs extends BaseMetricAggregation { export interface BasePipelineMetricAggregation extends MetricAggregationWithField { type: PipelineMetricAggregationType; + pipelineAgg?: string; } interface PipelineMetricAggregationWithMultipleBucketPaths extends BaseMetricAggregation { diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.test.ts b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.test.ts index 70872c96808..293c03efb87 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.test.ts +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.test.ts @@ -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', () => { diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.ts b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.ts index 760bb171cab..e89e586f565 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.ts +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.ts @@ -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: diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.tsx b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.tsx index 7ba7551cdd6..917ce74a74e 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.tsx +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/index.tsx @@ -12,8 +12,13 @@ import { useNextId } from '../../hooks/useNextId'; export type ElasticQueryEditorProps = QueryEditorProps; -export const QueryEditor: FunctionComponent = ({ query, onChange, datasource }) => ( - +export const QueryEditor: FunctionComponent = ({ + query, + onChange, + onRunQuery, + datasource, +}) => ( + ); diff --git a/public/app/plugins/datasource/elasticsearch/hooks/useNextId.test.tsx b/public/app/plugins/datasource/elasticsearch/hooks/useNextId.test.tsx index 8854edbc3c1..2d52a869312 100644 --- a/public/app/plugins/datasource/elasticsearch/hooks/useNextId.test.tsx +++ b/public/app/plugins/datasource/elasticsearch/hooks/useNextId.test.tsx @@ -13,7 +13,7 @@ describe('useNextId', () => { }; const wrapper: FunctionComponent = ({ children }) => { return ( - {}}> + {}} onRunQuery={() => {}}> {children} );