mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Reduce Elasticsearch data source plugin strict errors (#37399)
This commit is contained in:
parent
e0010860bd
commit
2f21bf5cfd
@ -7,7 +7,6 @@ import { segmentStyles } from '../styles';
|
||||
import { BucketAggregation, BucketAggregationType, isBucketAggregationWithField } from './aggregations';
|
||||
import { SettingsEditor } from './SettingsEditor';
|
||||
import { changeBucketAggregationField, changeBucketAggregationType } from './state/actions';
|
||||
import { BucketAggregationAction } from './state/types';
|
||||
import { bucketAggregationConfig } from './utils';
|
||||
|
||||
const bucketAggOptions: Array<SelectableValue<BucketAggregationType>> = Object.entries(bucketAggregationConfig).map(
|
||||
@ -27,7 +26,7 @@ interface QueryMetricEditorProps {
|
||||
}
|
||||
|
||||
export const BucketAggregationEditor = ({ value }: QueryMetricEditorProps) => {
|
||||
const dispatch = useDispatch<BucketAggregationAction>();
|
||||
const dispatch = useDispatch();
|
||||
const getFields = useFields(value.type);
|
||||
|
||||
return (
|
||||
@ -36,7 +35,7 @@ export const BucketAggregationEditor = ({ value }: QueryMetricEditorProps) => {
|
||||
<Segment
|
||||
className={segmentStyles}
|
||||
options={bucketAggOptions}
|
||||
onChange={(e) => dispatch(changeBucketAggregationType(value.id, e.value!))}
|
||||
onChange={(e) => dispatch(changeBucketAggregationType({ id: value.id, newType: e.value! }))}
|
||||
value={toOption(value)}
|
||||
/>
|
||||
|
||||
@ -44,7 +43,7 @@ export const BucketAggregationEditor = ({ value }: QueryMetricEditorProps) => {
|
||||
<SegmentAsync
|
||||
className={segmentStyles}
|
||||
loadOptions={getFields}
|
||||
onChange={(e) => dispatch(changeBucketAggregationField(value.id, e.value))}
|
||||
onChange={(e) => dispatch(changeBucketAggregationField({ id: value.id, newField: e.value }))}
|
||||
placeholder="Select Field"
|
||||
value={value.field}
|
||||
/>
|
||||
|
@ -46,7 +46,8 @@ interface Props {
|
||||
export const DateHistogramSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleIntervalChange = (v: string) => dispatch(changeBucketAggregationSetting(bucketAgg, 'interval', v));
|
||||
const handleIntervalChange = (newValue: string) =>
|
||||
dispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'interval', newValue }));
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -66,7 +67,11 @@ export const DateHistogramSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
|
||||
<InlineField label="Min Doc Count" {...inlineFieldProps}>
|
||||
<Input
|
||||
onBlur={(e) => dispatch(changeBucketAggregationSetting(bucketAgg, 'min_doc_count', e.target.value!))}
|
||||
onBlur={(e) =>
|
||||
dispatch(
|
||||
changeBucketAggregationSetting({ bucketAgg, settingName: 'min_doc_count', newValue: e.target.value })
|
||||
)
|
||||
}
|
||||
defaultValue={
|
||||
bucketAgg.settings?.min_doc_count || bucketAggregationConfig.date_histogram.defaultSettings?.min_doc_count
|
||||
}
|
||||
@ -75,7 +80,9 @@ export const DateHistogramSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
|
||||
<InlineField label="Trim Edges" {...inlineFieldProps} tooltip="Trim the edges on the timeseries datapoints">
|
||||
<Input
|
||||
onBlur={(e) => dispatch(changeBucketAggregationSetting(bucketAgg, 'trimEdges', e.target.value!))}
|
||||
onBlur={(e) =>
|
||||
dispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'trimEdges', newValue: e.target.value }))
|
||||
}
|
||||
defaultValue={
|
||||
bucketAgg.settings?.trimEdges || bucketAggregationConfig.date_histogram.defaultSettings?.trimEdges
|
||||
}
|
||||
@ -88,7 +95,9 @@ export const DateHistogramSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
tooltip="Change the start value of each bucket by the specified positive (+) or negative offset (-) duration, such as 1h for an hour, or 1d for a day"
|
||||
>
|
||||
<Input
|
||||
onBlur={(e) => dispatch(changeBucketAggregationSetting(bucketAgg, 'offset', e.target.value!))}
|
||||
onBlur={(e) =>
|
||||
dispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'offset', newValue: e.target.value }))
|
||||
}
|
||||
defaultValue={bucketAgg.settings?.offset || bucketAggregationConfig.date_histogram.defaultSettings?.offset}
|
||||
/>
|
||||
</InlineField>
|
||||
|
@ -5,7 +5,6 @@ import { AddRemove } from '../../../../AddRemove';
|
||||
import { useDispatch, useStatelessReducer } from '../../../../../hooks/useStatelessReducer';
|
||||
import { Filters } from '../../aggregations';
|
||||
import { changeBucketAggregationSetting } from '../../state/actions';
|
||||
import { BucketAggregationAction } from '../../state/types';
|
||||
import { addFilter, changeFilter, removeFilter } from './state/actions';
|
||||
import { reducer as filtersReducer } from './state/reducer';
|
||||
|
||||
@ -14,10 +13,10 @@ interface Props {
|
||||
}
|
||||
|
||||
export const FiltersSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
const upperStateDispatch = useDispatch<BucketAggregationAction<Filters>>();
|
||||
const upperStateDispatch = useDispatch();
|
||||
|
||||
const dispatch = useStatelessReducer(
|
||||
(newState) => upperStateDispatch(changeBucketAggregationSetting(bucketAgg, 'filters', newState)),
|
||||
(newValue) => upperStateDispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'filters', newValue })),
|
||||
bucketAgg.settings?.filters,
|
||||
filtersReducer
|
||||
);
|
||||
@ -55,7 +54,7 @@ export const FiltersSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
placeholder="Lucene Query"
|
||||
portalOrigin="elasticsearch"
|
||||
onBlur={() => {}}
|
||||
onChange={(query) => dispatch(changeFilter(index, { ...filter, query }))}
|
||||
onChange={(query) => dispatch(changeFilter({ index, filter: { ...filter, query } }))}
|
||||
query={filter.query}
|
||||
/>
|
||||
</InlineField>
|
||||
@ -63,7 +62,7 @@ export const FiltersSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
<InlineField label="Label" labelWidth={10}>
|
||||
<Input
|
||||
placeholder="Label"
|
||||
onBlur={(e) => dispatch(changeFilter(index, { ...filter, label: e.target.value }))}
|
||||
onBlur={(e) => dispatch(changeFilter({ index, filter: { ...filter, label: e.target.value } }))}
|
||||
defaultValue={filter.label}
|
||||
/>
|
||||
</InlineField>
|
||||
|
@ -1,16 +1,6 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import { Filter } from '../../../aggregations';
|
||||
import { FilterAction, ADD_FILTER, REMOVE_FILTER, CHANGE_FILTER } from './types';
|
||||
|
||||
export const addFilter = (): FilterAction => ({
|
||||
type: ADD_FILTER,
|
||||
});
|
||||
|
||||
export const removeFilter = (index: number): FilterAction => ({
|
||||
type: REMOVE_FILTER,
|
||||
payload: { index },
|
||||
});
|
||||
|
||||
export const changeFilter = (index: number, filter: Filter): FilterAction => ({
|
||||
type: CHANGE_FILTER,
|
||||
payload: { index, filter },
|
||||
});
|
||||
export const addFilter = createAction('@bucketAggregations/filter/add');
|
||||
export const removeFilter = createAction<number>('@bucketAggregations/filter/remove');
|
||||
export const changeFilter = createAction<{ index: number; filter: Filter }>('@bucketAggregations/filter/change');
|
||||
|
@ -46,7 +46,7 @@ describe('Filters Bucket Aggregation Settings Reducer', () => {
|
||||
|
||||
reducerTester<Filter[]>()
|
||||
.givenReducer(reducer, [firstFilter, secondFilter])
|
||||
.whenActionIsDispatched(changeFilter(1, expectedSecondFilter))
|
||||
.whenActionIsDispatched(changeFilter({ index: 1, filter: expectedSecondFilter }))
|
||||
.thenStateShouldEqual([firstFilter, expectedSecondFilter]);
|
||||
});
|
||||
});
|
||||
|
@ -1,21 +1,26 @@
|
||||
import { Action } from 'redux';
|
||||
import { Filter } from '../../../aggregations';
|
||||
import { defaultFilter } from '../utils';
|
||||
import { ADD_FILTER, CHANGE_FILTER, FilterAction, REMOVE_FILTER } from './types';
|
||||
import { addFilter, changeFilter, removeFilter } from './actions';
|
||||
|
||||
export const reducer = (state: Filter[] = [], action: FilterAction) => {
|
||||
switch (action.type) {
|
||||
case ADD_FILTER:
|
||||
return [...state, defaultFilter()];
|
||||
case REMOVE_FILTER:
|
||||
return state.slice(0, action.payload.index).concat(state.slice(action.payload.index + 1));
|
||||
|
||||
case CHANGE_FILTER:
|
||||
return state.map((filter, index) => {
|
||||
if (index !== action.payload.index) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
return action.payload.filter;
|
||||
});
|
||||
export const reducer = (state: Filter[] = [], action: Action) => {
|
||||
if (addFilter.match(action)) {
|
||||
return [...state, defaultFilter()];
|
||||
}
|
||||
|
||||
if (removeFilter.match(action)) {
|
||||
return state.slice(0, action.payload).concat(state.slice(action.payload + 1));
|
||||
}
|
||||
|
||||
if (changeFilter.match(action)) {
|
||||
return state.map((filter, index) => {
|
||||
if (index !== action.payload.index) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
return action.payload.filter;
|
||||
});
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
@ -1,22 +0,0 @@
|
||||
import { Action } from '../../../../../../hooks/useStatelessReducer';
|
||||
import { Filter } from '../../../aggregations';
|
||||
|
||||
export const ADD_FILTER = '@bucketAggregations/filter/add';
|
||||
export const REMOVE_FILTER = '@bucketAggregations/filter/remove';
|
||||
export const CHANGE_FILTER = '@bucketAggregations/filter/change';
|
||||
|
||||
export type AddFilterAction = Action<typeof ADD_FILTER>;
|
||||
|
||||
export interface RemoveFilterAction extends Action<typeof REMOVE_FILTER> {
|
||||
payload: {
|
||||
index: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangeFilterAction extends Action<typeof CHANGE_FILTER> {
|
||||
payload: {
|
||||
index: number;
|
||||
filter: Filter;
|
||||
};
|
||||
}
|
||||
export type FilterAction = AddFilterAction | RemoveFilterAction | ChangeFilterAction;
|
@ -23,7 +23,9 @@ export const TermsSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
<InlineField label="Order" {...inlineFieldProps}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
onChange={(e) => dispatch(changeBucketAggregationSetting(bucketAgg, 'order', e.value!))}
|
||||
onChange={(e) =>
|
||||
dispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'order', newValue: e.value }))
|
||||
}
|
||||
options={orderOptions}
|
||||
value={bucketAgg.settings?.order || bucketAggregationConfig.terms.defaultSettings?.order}
|
||||
/>
|
||||
@ -36,8 +38,8 @@ export const TermsSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
{...useCreatableSelectPersistedBehaviour({
|
||||
options: sizeOptions,
|
||||
value: bucketAgg.settings?.size || bucketAggregationConfig.terms.defaultSettings?.size,
|
||||
onChange(value) {
|
||||
dispatch(changeBucketAggregationSetting(bucketAgg, 'size', value));
|
||||
onChange(newValue) {
|
||||
dispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'size', newValue }));
|
||||
},
|
||||
})}
|
||||
/>
|
||||
@ -45,7 +47,11 @@ export const TermsSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
|
||||
<InlineField label="Min Doc Count" {...inlineFieldProps}>
|
||||
<Input
|
||||
onBlur={(e) => dispatch(changeBucketAggregationSetting(bucketAgg, 'min_doc_count', e.target.value!))}
|
||||
onBlur={(e) =>
|
||||
dispatch(
|
||||
changeBucketAggregationSetting({ bucketAgg, settingName: 'min_doc_count', newValue: e.target.value })
|
||||
)
|
||||
}
|
||||
defaultValue={
|
||||
bucketAgg.settings?.min_doc_count || bucketAggregationConfig.terms.defaultSettings?.min_doc_count
|
||||
}
|
||||
@ -55,7 +61,9 @@ export const TermsSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
<InlineField label="Order By" {...inlineFieldProps}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
onChange={(e) => dispatch(changeBucketAggregationSetting(bucketAgg, 'orderBy', e.value!))}
|
||||
onChange={(e) =>
|
||||
dispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'orderBy', newValue: e.value }))
|
||||
}
|
||||
options={orderBy}
|
||||
value={bucketAgg.settings?.orderBy || bucketAggregationConfig.terms.defaultSettings?.orderBy}
|
||||
/>
|
||||
@ -63,7 +71,9 @@ export const TermsSettingsEditor = ({ bucketAgg }: Props) => {
|
||||
|
||||
<InlineField label="Missing" {...inlineFieldProps}>
|
||||
<Input
|
||||
onBlur={(e) => dispatch(changeBucketAggregationSetting(bucketAgg, 'missing', e.target.value!))}
|
||||
onBlur={(e) =>
|
||||
dispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'missing', newValue: e.target.value }))
|
||||
}
|
||||
defaultValue={bucketAgg.settings?.missing || bucketAggregationConfig.terms.defaultSettings?.missing}
|
||||
/>
|
||||
</InlineField>
|
||||
|
@ -32,7 +32,11 @@ export const SettingsEditor = ({ bucketAgg }: Props) => {
|
||||
{bucketAgg.type === 'geohash_grid' && (
|
||||
<InlineField label="Precision" {...inlineFieldProps}>
|
||||
<Input
|
||||
onBlur={(e) => dispatch(changeBucketAggregationSetting(bucketAgg, 'precision', e.target.value!))}
|
||||
onBlur={(e) =>
|
||||
dispatch(
|
||||
changeBucketAggregationSetting({ bucketAgg, settingName: 'precision', newValue: e.target.value })
|
||||
)
|
||||
}
|
||||
defaultValue={
|
||||
bucketAgg.settings?.precision || bucketAggregationConfig[bucketAgg.type].defaultSettings?.precision
|
||||
}
|
||||
@ -44,7 +48,11 @@ export const SettingsEditor = ({ bucketAgg }: Props) => {
|
||||
<>
|
||||
<InlineField label="Interval" {...inlineFieldProps}>
|
||||
<Input
|
||||
onBlur={(e) => dispatch(changeBucketAggregationSetting(bucketAgg, 'interval', e.target.value!))}
|
||||
onBlur={(e) =>
|
||||
dispatch(
|
||||
changeBucketAggregationSetting({ bucketAgg, settingName: 'interval', newValue: e.target.value })
|
||||
)
|
||||
}
|
||||
defaultValue={
|
||||
bucketAgg.settings?.interval || bucketAggregationConfig[bucketAgg.type].defaultSettings?.interval
|
||||
}
|
||||
@ -53,7 +61,11 @@ export const SettingsEditor = ({ bucketAgg }: Props) => {
|
||||
|
||||
<InlineField label="Min Doc Count" {...inlineFieldProps}>
|
||||
<Input
|
||||
onBlur={(e) => dispatch(changeBucketAggregationSetting(bucketAgg, 'min_doc_count', e.target.value!))}
|
||||
onBlur={(e) =>
|
||||
dispatch(
|
||||
changeBucketAggregationSetting({ bucketAgg, settingName: 'min_doc_count', newValue: e.target.value })
|
||||
)
|
||||
}
|
||||
defaultValue={
|
||||
bucketAgg.settings?.min_doc_count ||
|
||||
bucketAggregationConfig[bucketAgg.type].defaultSettings?.min_doc_count
|
||||
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { BucketAggregationEditor } from './BucketAggregationEditor';
|
||||
import { useDispatch } from '../../../hooks/useStatelessReducer';
|
||||
import { addBucketAggregation, removeBucketAggregation } from './state/actions';
|
||||
import { BucketAggregationAction } from './state/types';
|
||||
import { BucketAggregation } from './aggregations';
|
||||
import { useQuery } from '../ElasticsearchQueryContext';
|
||||
import { QueryEditorRow } from '../QueryEditorRow';
|
||||
@ -13,7 +12,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const BucketAggregationsEditor = ({ nextId }: Props) => {
|
||||
const dispatch = useDispatch<BucketAggregationAction>();
|
||||
const dispatch = useDispatch();
|
||||
const { bucketAggs } = useQuery();
|
||||
const totalBucketAggs = bucketAggs?.length || 0;
|
||||
|
||||
|
@ -1,61 +1,18 @@
|
||||
import { SettingKeyOf } from '../../../types';
|
||||
import { BucketAggregation, BucketAggregationWithField } from '../aggregations';
|
||||
import {
|
||||
ADD_BUCKET_AGG,
|
||||
BucketAggregationAction,
|
||||
REMOVE_BUCKET_AGG,
|
||||
CHANGE_BUCKET_AGG_TYPE,
|
||||
CHANGE_BUCKET_AGG_FIELD,
|
||||
CHANGE_BUCKET_AGG_SETTING,
|
||||
ChangeBucketAggregationSettingAction,
|
||||
} from './types';
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import { BucketAggregation, BucketAggregationType, BucketAggregationWithField } from '../aggregations';
|
||||
|
||||
export const addBucketAggregation = (id: string): BucketAggregationAction => ({
|
||||
type: ADD_BUCKET_AGG,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
export const removeBucketAggregation = (id: BucketAggregation['id']): BucketAggregationAction => ({
|
||||
type: REMOVE_BUCKET_AGG,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
export const changeBucketAggregationType = (
|
||||
id: BucketAggregation['id'],
|
||||
newType: BucketAggregation['type']
|
||||
): BucketAggregationAction => ({
|
||||
type: CHANGE_BUCKET_AGG_TYPE,
|
||||
payload: {
|
||||
id,
|
||||
newType,
|
||||
},
|
||||
});
|
||||
|
||||
export const changeBucketAggregationField = (
|
||||
id: BucketAggregationWithField['id'],
|
||||
newField: BucketAggregationWithField['field']
|
||||
): BucketAggregationAction => ({
|
||||
type: CHANGE_BUCKET_AGG_FIELD,
|
||||
payload: {
|
||||
id,
|
||||
newField,
|
||||
},
|
||||
});
|
||||
|
||||
export const changeBucketAggregationSetting = <T extends BucketAggregation, K extends SettingKeyOf<T>>(
|
||||
bucketAgg: T,
|
||||
settingName: K,
|
||||
// This could be inferred from T, but it's causing some troubles
|
||||
newValue: string | string[] | any
|
||||
): ChangeBucketAggregationSettingAction<T> => ({
|
||||
type: CHANGE_BUCKET_AGG_SETTING,
|
||||
payload: {
|
||||
bucketAgg,
|
||||
settingName,
|
||||
newValue,
|
||||
},
|
||||
});
|
||||
export const addBucketAggregation = createAction<BucketAggregation['id']>('@bucketAggs/add');
|
||||
export const removeBucketAggregation = createAction<BucketAggregation['id']>('@bucketAggs/remove');
|
||||
export const changeBucketAggregationType = createAction<{
|
||||
id: BucketAggregation['id'];
|
||||
newType: BucketAggregationType;
|
||||
}>('@bucketAggs/change_type');
|
||||
export const changeBucketAggregationField = createAction<{
|
||||
id: BucketAggregation['id'];
|
||||
newField: BucketAggregationWithField['field'];
|
||||
}>('@bucketAggs/change_field');
|
||||
export const changeBucketAggregationSetting = createAction<{
|
||||
bucketAgg: BucketAggregation;
|
||||
settingName: string;
|
||||
newValue: any;
|
||||
}>('@bucketAggs/change_setting');
|
||||
|
@ -71,7 +71,9 @@ describe('Bucket Aggregations Reducer', () => {
|
||||
|
||||
reducerTester<ElasticsearchQuery['bucketAggs']>()
|
||||
.givenReducer(createReducer('@timestamp'), [firstAggregation, secondAggregation])
|
||||
.whenActionIsDispatched(changeBucketAggregationType(secondAggregation.id, expectedSecondAggregation.type))
|
||||
.whenActionIsDispatched(
|
||||
changeBucketAggregationType({ id: secondAggregation.id, newType: expectedSecondAggregation.type })
|
||||
)
|
||||
.thenStateShouldEqual([firstAggregation, expectedSecondAggregation]);
|
||||
});
|
||||
|
||||
@ -92,7 +94,9 @@ describe('Bucket Aggregations Reducer', () => {
|
||||
|
||||
reducerTester<ElasticsearchQuery['bucketAggs']>()
|
||||
.givenReducer(createReducer('@timestamp'), [firstAggregation, secondAggregation])
|
||||
.whenActionIsDispatched(changeBucketAggregationField(secondAggregation.id, expectedSecondAggregation.field))
|
||||
.whenActionIsDispatched(
|
||||
changeBucketAggregationField({ id: secondAggregation.id, newField: expectedSecondAggregation.field })
|
||||
)
|
||||
.thenStateShouldEqual([firstAggregation, expectedSecondAggregation]);
|
||||
});
|
||||
|
||||
@ -108,13 +112,13 @@ describe('Bucket Aggregations Reducer', () => {
|
||||
reducerTester<ElasticsearchQuery['bucketAggs']>()
|
||||
.givenReducer(createReducer('@timestamp'), initialState)
|
||||
// If the new metric aggregation is `isSingleMetric` we should remove all bucket aggregations.
|
||||
.whenActionIsDispatched(changeMetricType('Some id', 'raw_data'))
|
||||
.whenActionIsDispatched(changeMetricType({ id: 'Some id', type: 'raw_data' }))
|
||||
.thenStatePredicateShouldEqual((newState) => newState?.length === 0)
|
||||
// Switching back to another aggregation that is NOT `isSingleMetric` should bring back a bucket aggregation
|
||||
.whenActionIsDispatched(changeMetricType('Some id', 'max'))
|
||||
.whenActionIsDispatched(changeMetricType({ id: 'Some id', type: 'max' }))
|
||||
.thenStatePredicateShouldEqual((newState) => newState?.length === 1)
|
||||
// When none of the above is true state shouldn't change.
|
||||
.whenActionIsDispatched(changeMetricType('Some id', 'min'))
|
||||
.whenActionIsDispatched(changeMetricType({ id: 'Some id', type: 'min' }))
|
||||
.thenStatePredicateShouldEqual((newState) => newState?.length === 1);
|
||||
});
|
||||
});
|
||||
@ -139,7 +143,11 @@ describe('Bucket Aggregations Reducer', () => {
|
||||
reducerTester<ElasticsearchQuery['bucketAggs']>()
|
||||
.givenReducer(createReducer('@timestamp'), [firstAggregation, secondAggregation])
|
||||
.whenActionIsDispatched(
|
||||
changeBucketAggregationSetting(firstAggregation, 'min_doc_count', expectedSettings.min_doc_count!)
|
||||
changeBucketAggregationSetting({
|
||||
bucketAgg: firstAggregation,
|
||||
settingName: 'min_doc_count',
|
||||
newValue: expectedSettings.min_doc_count!,
|
||||
})
|
||||
)
|
||||
.thenStateShouldEqual([{ ...firstAggregation, settings: expectedSettings }, secondAggregation]);
|
||||
});
|
||||
|
@ -1,114 +1,118 @@
|
||||
import { defaultBucketAgg } from '../../../../query_def';
|
||||
import { ElasticsearchQuery } from '../../../../types';
|
||||
import { ChangeMetricTypeAction, CHANGE_METRIC_TYPE } from '../../MetricAggregationsEditor/state/types';
|
||||
import { metricAggregationConfig } from '../../MetricAggregationsEditor/utils';
|
||||
import { BucketAggregation, Terms } from '../aggregations';
|
||||
import { INIT, InitAction } from '../../state';
|
||||
import {
|
||||
ADD_BUCKET_AGG,
|
||||
REMOVE_BUCKET_AGG,
|
||||
CHANGE_BUCKET_AGG_TYPE,
|
||||
CHANGE_BUCKET_AGG_FIELD,
|
||||
CHANGE_BUCKET_AGG_SETTING,
|
||||
BucketAggregationAction,
|
||||
} from './types';
|
||||
import { initQuery } from '../../state';
|
||||
import { bucketAggregationConfig } from '../utils';
|
||||
import { removeEmpty } from '../../../../utils';
|
||||
import { Action } from '@reduxjs/toolkit';
|
||||
import {
|
||||
addBucketAggregation,
|
||||
changeBucketAggregationField,
|
||||
changeBucketAggregationSetting,
|
||||
changeBucketAggregationType,
|
||||
removeBucketAggregation,
|
||||
} from './actions';
|
||||
import { changeMetricType } from '../../MetricAggregationsEditor/state/actions';
|
||||
|
||||
export const createReducer = (defaultTimeField: string) => (
|
||||
state: ElasticsearchQuery['bucketAggs'],
|
||||
action: BucketAggregationAction | ChangeMetricTypeAction | InitAction
|
||||
action: Action
|
||||
): ElasticsearchQuery['bucketAggs'] => {
|
||||
switch (action.type) {
|
||||
case ADD_BUCKET_AGG:
|
||||
const newAgg: Terms = {
|
||||
id: action.payload.id,
|
||||
type: 'terms',
|
||||
settings: bucketAggregationConfig['terms'].defaultSettings,
|
||||
};
|
||||
if (addBucketAggregation.match(action)) {
|
||||
const newAgg: Terms = {
|
||||
id: action.payload,
|
||||
type: 'terms',
|
||||
settings: bucketAggregationConfig['terms'].defaultSettings,
|
||||
};
|
||||
|
||||
// If the last bucket aggregation is a `date_histogram` we add the new one before it.
|
||||
const lastAgg = state![state!.length - 1];
|
||||
if (lastAgg?.type === 'date_histogram') {
|
||||
return [...state!.slice(0, state!.length - 1), newAgg, lastAgg];
|
||||
}
|
||||
// If the last bucket aggregation is a `date_histogram` we add the new one before it.
|
||||
const lastAgg = state![state!.length - 1];
|
||||
if (lastAgg?.type === 'date_histogram') {
|
||||
return [...state!.slice(0, state!.length - 1), newAgg, lastAgg];
|
||||
}
|
||||
|
||||
return [...state!, newAgg];
|
||||
|
||||
case REMOVE_BUCKET_AGG:
|
||||
return state!.filter((bucketAgg) => bucketAgg.id !== action.payload.id);
|
||||
|
||||
case CHANGE_BUCKET_AGG_TYPE:
|
||||
return state!.map((bucketAgg) => {
|
||||
if (bucketAgg.id !== action.payload.id) {
|
||||
return bucketAgg;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: The previous version of the query editor was keeping some of the old bucket aggregation's configurations
|
||||
in the new selected one (such as field or some settings).
|
||||
It the future would be nice to have the same behavior but it's hard without a proper definition,
|
||||
as Elasticsearch will error sometimes if some settings are not compatible.
|
||||
*/
|
||||
return {
|
||||
id: bucketAgg.id,
|
||||
type: action.payload.newType,
|
||||
settings: bucketAggregationConfig[action.payload.newType].defaultSettings,
|
||||
} as BucketAggregation;
|
||||
});
|
||||
|
||||
case CHANGE_BUCKET_AGG_FIELD:
|
||||
return state!.map((bucketAgg) => {
|
||||
if (bucketAgg.id !== action.payload.id) {
|
||||
return bucketAgg;
|
||||
}
|
||||
|
||||
return {
|
||||
...bucketAgg,
|
||||
field: action.payload.newField,
|
||||
};
|
||||
});
|
||||
|
||||
case CHANGE_METRIC_TYPE:
|
||||
// If we are switching to a metric which requires the absence of bucket aggregations
|
||||
// we remove all of them.
|
||||
if (metricAggregationConfig[action.payload.type].isSingleMetric) {
|
||||
return [];
|
||||
} else if (state!.length === 0) {
|
||||
// Else, if there are no bucket aggregations we restore a default one.
|
||||
// This happens when switching from a metric that requires the absence of bucket aggregations to
|
||||
// one that requires it.
|
||||
return [{ ...defaultBucketAgg('2'), field: defaultTimeField }];
|
||||
}
|
||||
return state;
|
||||
|
||||
case CHANGE_BUCKET_AGG_SETTING:
|
||||
return state!.map((bucketAgg) => {
|
||||
if (bucketAgg.id !== action.payload.bucketAgg.id) {
|
||||
return bucketAgg;
|
||||
}
|
||||
|
||||
const newSettings = removeEmpty({
|
||||
...bucketAgg.settings,
|
||||
[action.payload.settingName]: action.payload.newValue,
|
||||
});
|
||||
|
||||
return {
|
||||
...bucketAgg,
|
||||
settings: {
|
||||
...newSettings,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
case INIT:
|
||||
if (state?.length || 0 > 0) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return [{ ...defaultBucketAgg('2'), field: defaultTimeField }];
|
||||
|
||||
default:
|
||||
return state;
|
||||
return [...state!, newAgg];
|
||||
}
|
||||
|
||||
if (removeBucketAggregation.match(action)) {
|
||||
return state!.filter((bucketAgg) => bucketAgg.id !== action.payload);
|
||||
}
|
||||
|
||||
if (changeBucketAggregationType.match(action)) {
|
||||
return state!.map((bucketAgg) => {
|
||||
if (bucketAgg.id !== action.payload.id) {
|
||||
return bucketAgg;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: The previous version of the query editor was keeping some of the old bucket aggregation's configurations
|
||||
in the new selected one (such as field or some settings).
|
||||
It the future would be nice to have the same behavior but it's hard without a proper definition,
|
||||
as Elasticsearch will error sometimes if some settings are not compatible.
|
||||
*/
|
||||
return {
|
||||
id: bucketAgg.id,
|
||||
type: action.payload.newType,
|
||||
settings: bucketAggregationConfig[action.payload.newType].defaultSettings,
|
||||
} as BucketAggregation;
|
||||
});
|
||||
}
|
||||
|
||||
if (changeBucketAggregationField.match(action)) {
|
||||
return state!.map((bucketAgg) => {
|
||||
if (bucketAgg.id !== action.payload.id) {
|
||||
return bucketAgg;
|
||||
}
|
||||
|
||||
return {
|
||||
...bucketAgg,
|
||||
field: action.payload.newField,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (changeMetricType.match(action)) {
|
||||
// If we are switching to a metric which requires the absence of bucket aggregations
|
||||
// we remove all of them.
|
||||
if (metricAggregationConfig[action.payload.type].isSingleMetric) {
|
||||
return [];
|
||||
} else if (state!.length === 0) {
|
||||
// Else, if there are no bucket aggregations we restore a default one.
|
||||
// This happens when switching from a metric that requires the absence of bucket aggregations to
|
||||
// one that requires it.
|
||||
return [{ ...defaultBucketAgg('2'), field: defaultTimeField }];
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
if (changeBucketAggregationSetting.match(action)) {
|
||||
return state!.map((bucketAgg) => {
|
||||
if (bucketAgg.id !== action.payload.bucketAgg.id) {
|
||||
return bucketAgg;
|
||||
}
|
||||
|
||||
const newSettings = removeEmpty({
|
||||
...bucketAgg.settings,
|
||||
[action.payload.settingName]: action.payload.newValue,
|
||||
});
|
||||
|
||||
return {
|
||||
...bucketAgg,
|
||||
settings: {
|
||||
...newSettings,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (initQuery.match(action)) {
|
||||
if (state?.length || 0 > 0) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return [{ ...defaultBucketAgg('2'), field: defaultTimeField }];
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
@ -1,51 +0,0 @@
|
||||
import { Action } from '../../../../hooks/useStatelessReducer';
|
||||
import { SettingKeyOf } from '../../../types';
|
||||
import { BucketAggregation, BucketAggregationWithField } from '../aggregations';
|
||||
|
||||
export const ADD_BUCKET_AGG = '@bucketAggs/add';
|
||||
export const REMOVE_BUCKET_AGG = '@bucketAggs/remove';
|
||||
export const CHANGE_BUCKET_AGG_TYPE = '@bucketAggs/change_type';
|
||||
export const CHANGE_BUCKET_AGG_FIELD = '@bucketAggs/change_field';
|
||||
export const CHANGE_BUCKET_AGG_SETTING = '@bucketAggs/change_setting';
|
||||
|
||||
export interface AddBucketAggregationAction extends Action<typeof ADD_BUCKET_AGG> {
|
||||
payload: {
|
||||
id: BucketAggregation['id'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface RemoveBucketAggregationAction extends Action<typeof REMOVE_BUCKET_AGG> {
|
||||
payload: {
|
||||
id: BucketAggregation['id'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangeBucketAggregationTypeAction extends Action<typeof CHANGE_BUCKET_AGG_TYPE> {
|
||||
payload: {
|
||||
id: BucketAggregation['id'];
|
||||
newType: BucketAggregation['type'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangeBucketAggregationFieldAction extends Action<typeof CHANGE_BUCKET_AGG_FIELD> {
|
||||
payload: {
|
||||
id: BucketAggregation['id'];
|
||||
newField: BucketAggregationWithField['field'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangeBucketAggregationSettingAction<T extends BucketAggregation>
|
||||
extends Action<typeof CHANGE_BUCKET_AGG_SETTING> {
|
||||
payload: {
|
||||
bucketAgg: T;
|
||||
settingName: SettingKeyOf<T>;
|
||||
newValue: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
export type BucketAggregationAction<T extends BucketAggregation = BucketAggregation> =
|
||||
| AddBucketAggregationAction
|
||||
| RemoveBucketAggregationAction
|
||||
| ChangeBucketAggregationTypeAction
|
||||
| ChangeBucketAggregationFieldAction
|
||||
| ChangeBucketAggregationSettingAction<T>;
|
@ -79,7 +79,7 @@ export const orderByOptions = [
|
||||
/**
|
||||
* This returns the valid options for each of the enabled extended stat
|
||||
*/
|
||||
function createOrderByOptionsForExtendedStats(metric: ExtendedStats): OrderByOption[] {
|
||||
function createOrderByOptionsForExtendedStats(metric: ExtendedStats): SelectableValue<string> {
|
||||
if (!metric.meta) {
|
||||
return [];
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import { useDatasource, useQuery } from '../ElasticsearchQueryContext';
|
||||
import { useDispatch } from '../../../hooks/useStatelessReducer';
|
||||
import { getStyles } from './styles';
|
||||
import { SettingsEditor } from './SettingsEditor';
|
||||
import { MetricAggregationAction } from './state/types';
|
||||
import { metricAggregationConfig } from './utils';
|
||||
import { changeMetricField, changeMetricType } from './state/actions';
|
||||
import { MetricPicker } from '../../MetricPicker';
|
||||
@ -65,7 +64,7 @@ export const MetricEditor = ({ value }: Props) => {
|
||||
const styles = getStyles(useTheme2(), !!value.hide);
|
||||
const datasource = useDatasource();
|
||||
const query = useQuery();
|
||||
const dispatch = useDispatch<MetricAggregationAction>();
|
||||
const dispatch = useDispatch();
|
||||
const getFields = useFields(value.type);
|
||||
|
||||
const loadOptions = useCallback(async () => {
|
||||
@ -90,7 +89,7 @@ export const MetricEditor = ({ value }: Props) => {
|
||||
<Segment
|
||||
className={cx(styles.color, segmentStyles)}
|
||||
options={getTypeOptions(previousMetrics, datasource.esVersion, datasource.xpack)}
|
||||
onChange={(e) => dispatch(changeMetricType(value.id, e.value!))}
|
||||
onChange={(e) => dispatch(changeMetricType({ id: value.id, type: e.value! }))}
|
||||
value={toOption(value)}
|
||||
/>
|
||||
|
||||
@ -98,7 +97,7 @@ export const MetricEditor = ({ value }: Props) => {
|
||||
<SegmentAsync
|
||||
className={cx(styles.color, segmentStyles)}
|
||||
loadOptions={loadOptions}
|
||||
onChange={(e) => dispatch(changeMetricField(value.id, e.value!))}
|
||||
onChange={(e) => dispatch(changeMetricField({ id: value.id, field: e.value! }))}
|
||||
placeholder="Select Field"
|
||||
value={value.field}
|
||||
/>
|
||||
@ -107,7 +106,7 @@ export const MetricEditor = ({ value }: Props) => {
|
||||
{isPipelineAggregation(value) && !isPipelineAggregationWithMultipleBucketPaths(value) && (
|
||||
<MetricPicker
|
||||
className={cx(styles.color, segmentStyles)}
|
||||
onChange={(e) => dispatch(changeMetricField(value.id, e.value?.id!))}
|
||||
onChange={(e) => dispatch(changeMetricField({ id: value.id, field: e.value?.id! }))}
|
||||
options={previousMetrics}
|
||||
value={value.field}
|
||||
/>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { Fragment, useEffect } from 'react';
|
||||
import { Input, InlineLabel } from '@grafana/ui';
|
||||
import { MetricAggregationAction } from '../../state/types';
|
||||
import { changeMetricAttribute } from '../../state/actions';
|
||||
import { css } from '@emotion/css';
|
||||
import { AddRemove } from '../../../../AddRemove';
|
||||
@ -23,10 +22,11 @@ interface Props {
|
||||
}
|
||||
|
||||
export const BucketScriptSettingsEditor = ({ value, previousMetrics }: Props) => {
|
||||
const upperStateDispatch = useDispatch<MetricAggregationAction<BucketScript>>();
|
||||
const upperStateDispatch = useDispatch();
|
||||
|
||||
const dispatch = useStatelessReducer(
|
||||
(newState) => upperStateDispatch(changeMetricAttribute(value, 'pipelineVariables', newState)),
|
||||
(newValue) =>
|
||||
upperStateDispatch(changeMetricAttribute({ metric: value, attribute: 'pipelineVariables', newValue })),
|
||||
value.pipelineVariables,
|
||||
reducer
|
||||
);
|
||||
@ -74,10 +74,10 @@ export const BucketScriptSettingsEditor = ({ value, previousMetrics }: Props) =>
|
||||
<Input
|
||||
defaultValue={pipelineVar.name}
|
||||
placeholder="Variable Name"
|
||||
onBlur={(e) => dispatch(renamePipelineVariable(e.target.value, index))}
|
||||
onBlur={(e) => dispatch(renamePipelineVariable({ newName: e.target.value, index }))}
|
||||
/>
|
||||
<MetricPicker
|
||||
onChange={(e) => dispatch(changePipelineVariableMetric(e.value!.id, index))}
|
||||
onChange={(e) => dispatch(changePipelineVariableMetric({ newMetric: e.value!.id, index }))}
|
||||
options={previousMetrics}
|
||||
value={pipelineVar.pipelineAgg}
|
||||
/>
|
||||
|
@ -1,34 +1,10 @@
|
||||
import {
|
||||
ADD_PIPELINE_VARIABLE,
|
||||
REMOVE_PIPELINE_VARIABLE,
|
||||
PipelineVariablesAction,
|
||||
RENAME_PIPELINE_VARIABLE,
|
||||
CHANGE_PIPELINE_VARIABLE_METRIC,
|
||||
} from './types';
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
export const addPipelineVariable = (): PipelineVariablesAction => ({
|
||||
type: ADD_PIPELINE_VARIABLE,
|
||||
});
|
||||
export const addPipelineVariable = createAction('@pipelineVariables/add');
|
||||
export const removePipelineVariable = createAction<number>('@pipelineVariables/remove');
|
||||
|
||||
export const removePipelineVariable = (index: number): PipelineVariablesAction => ({
|
||||
type: REMOVE_PIPELINE_VARIABLE,
|
||||
payload: {
|
||||
index,
|
||||
},
|
||||
});
|
||||
export const renamePipelineVariable = createAction<{ index: number; newName: string }>('@pipelineVariables/rename');
|
||||
|
||||
export const renamePipelineVariable = (newName: string, index: number): PipelineVariablesAction => ({
|
||||
type: RENAME_PIPELINE_VARIABLE,
|
||||
payload: {
|
||||
index,
|
||||
newName,
|
||||
},
|
||||
});
|
||||
|
||||
export const changePipelineVariableMetric = (newMetric: string, index: number): PipelineVariablesAction => ({
|
||||
type: CHANGE_PIPELINE_VARIABLE_METRIC,
|
||||
payload: {
|
||||
index,
|
||||
newMetric,
|
||||
},
|
||||
});
|
||||
export const changePipelineVariableMetric = createAction<{ index: number; newMetric: string }>(
|
||||
'@pipelineVariables/change_metric'
|
||||
);
|
||||
|
@ -72,7 +72,7 @@ describe('BucketScript Settings Reducer', () => {
|
||||
|
||||
reducerTester<PipelineVariable[]>()
|
||||
.givenReducer(reducer, [firstVar, secondVar])
|
||||
.whenActionIsDispatched(renamePipelineVariable(expectedSecondVar.name, 1))
|
||||
.whenActionIsDispatched(renamePipelineVariable({ newName: expectedSecondVar.name, index: 1 }))
|
||||
.thenStateShouldEqual([firstVar, expectedSecondVar]);
|
||||
});
|
||||
|
||||
@ -94,7 +94,7 @@ describe('BucketScript Settings Reducer', () => {
|
||||
|
||||
reducerTester<PipelineVariable[]>()
|
||||
.givenReducer(reducer, [firstVar, secondVar])
|
||||
.whenActionIsDispatched(changePipelineVariableMetric(expectedSecondVar.pipelineAgg, 1))
|
||||
.whenActionIsDispatched(changePipelineVariableMetric({ newMetric: expectedSecondVar.pipelineAgg, index: 1 }))
|
||||
.thenStateShouldEqual([firstVar, expectedSecondVar]);
|
||||
});
|
||||
|
||||
|
@ -1,46 +1,47 @@
|
||||
import { Action } from '@reduxjs/toolkit';
|
||||
import { PipelineVariable } from '../../../aggregations';
|
||||
import { defaultPipelineVariable, generatePipelineVariableName } from '../utils';
|
||||
import {
|
||||
PipelineVariablesAction,
|
||||
REMOVE_PIPELINE_VARIABLE,
|
||||
ADD_PIPELINE_VARIABLE,
|
||||
RENAME_PIPELINE_VARIABLE,
|
||||
CHANGE_PIPELINE_VARIABLE_METRIC,
|
||||
} from './types';
|
||||
addPipelineVariable,
|
||||
changePipelineVariableMetric,
|
||||
removePipelineVariable,
|
||||
renamePipelineVariable,
|
||||
} from './actions';
|
||||
|
||||
export const reducer = (state: PipelineVariable[] = [], action: PipelineVariablesAction) => {
|
||||
switch (action.type) {
|
||||
case ADD_PIPELINE_VARIABLE:
|
||||
return [...state, defaultPipelineVariable(generatePipelineVariableName(state))];
|
||||
|
||||
case REMOVE_PIPELINE_VARIABLE:
|
||||
return state.slice(0, action.payload.index).concat(state.slice(action.payload.index + 1));
|
||||
|
||||
case RENAME_PIPELINE_VARIABLE:
|
||||
return state.map((pipelineVariable, index) => {
|
||||
if (index !== action.payload.index) {
|
||||
return pipelineVariable;
|
||||
}
|
||||
|
||||
return {
|
||||
...pipelineVariable,
|
||||
name: action.payload.newName,
|
||||
};
|
||||
});
|
||||
|
||||
case CHANGE_PIPELINE_VARIABLE_METRIC:
|
||||
return state.map((pipelineVariable, index) => {
|
||||
if (index !== action.payload.index) {
|
||||
return pipelineVariable;
|
||||
}
|
||||
|
||||
return {
|
||||
...pipelineVariable,
|
||||
pipelineAgg: action.payload.newMetric,
|
||||
};
|
||||
});
|
||||
|
||||
default:
|
||||
return state;
|
||||
export const reducer = (state: PipelineVariable[] = [], action: Action) => {
|
||||
if (addPipelineVariable.match(action)) {
|
||||
return [...state, defaultPipelineVariable(generatePipelineVariableName(state))];
|
||||
}
|
||||
|
||||
if (removePipelineVariable.match(action)) {
|
||||
return state.slice(0, action.payload).concat(state.slice(action.payload + 1));
|
||||
}
|
||||
|
||||
if (renamePipelineVariable.match(action)) {
|
||||
return state.map((pipelineVariable, index) => {
|
||||
if (index !== action.payload.index) {
|
||||
return pipelineVariable;
|
||||
}
|
||||
|
||||
return {
|
||||
...pipelineVariable,
|
||||
name: action.payload.newName,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (changePipelineVariableMetric.match(action)) {
|
||||
return state.map((pipelineVariable, index) => {
|
||||
if (index !== action.payload.index) {
|
||||
return pipelineVariable;
|
||||
}
|
||||
|
||||
return {
|
||||
...pipelineVariable,
|
||||
pipelineAgg: action.payload.newMetric,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
@ -1,34 +0,0 @@
|
||||
import { Action } from '../../../../../../hooks/useStatelessReducer';
|
||||
|
||||
export const ADD_PIPELINE_VARIABLE = '@pipelineVariables/add';
|
||||
export const REMOVE_PIPELINE_VARIABLE = '@pipelineVariables/remove';
|
||||
export const RENAME_PIPELINE_VARIABLE = '@pipelineVariables/rename';
|
||||
export const CHANGE_PIPELINE_VARIABLE_METRIC = '@pipelineVariables/change_metric';
|
||||
|
||||
export type AddPipelineVariableAction = Action<typeof ADD_PIPELINE_VARIABLE>;
|
||||
|
||||
export interface RemovePipelineVariableAction extends Action<typeof REMOVE_PIPELINE_VARIABLE> {
|
||||
payload: {
|
||||
index: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RenamePipelineVariableAction extends Action<typeof RENAME_PIPELINE_VARIABLE> {
|
||||
payload: {
|
||||
index: number;
|
||||
newName: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangePipelineVariableMetricAction extends Action<typeof CHANGE_PIPELINE_VARIABLE_METRIC> {
|
||||
payload: {
|
||||
index: number;
|
||||
newMetric: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type PipelineVariablesAction =
|
||||
| AddPipelineVariableAction
|
||||
| RemovePipelineVariableAction
|
||||
| RenamePipelineVariableAction
|
||||
| ChangePipelineVariableMetricAction;
|
@ -21,7 +21,7 @@ export const MovingAverageSettingsEditor = ({ metric }: Props) => {
|
||||
<InlineField label="Model" labelWidth={16}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
onChange={(value) => dispatch(changeMetricSetting(metric, 'model', value.value!))}
|
||||
onChange={(value) => dispatch(changeMetricSetting({ metric, settingName: 'model', newValue: value.value }))}
|
||||
options={movingAvgModelOptions}
|
||||
value={metric.settings?.model}
|
||||
/>
|
||||
@ -36,9 +36,13 @@ export const MovingAverageSettingsEditor = ({ metric }: Props) => {
|
||||
<Input
|
||||
onBlur={(e) =>
|
||||
dispatch(
|
||||
changeMetricSetting(metric, 'settings', {
|
||||
...metric.settings?.settings,
|
||||
alpha: e.target.value,
|
||||
changeMetricSetting({
|
||||
metric,
|
||||
settingName: 'settings',
|
||||
newValue: {
|
||||
...metric.settings?.settings,
|
||||
alpha: e.target.value,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -52,9 +56,13 @@ export const MovingAverageSettingsEditor = ({ metric }: Props) => {
|
||||
<Input
|
||||
onBlur={(e) =>
|
||||
dispatch(
|
||||
changeMetricSetting(metric, 'settings', {
|
||||
...metric.settings?.settings,
|
||||
beta: e.target.value,
|
||||
changeMetricSetting({
|
||||
metric,
|
||||
settingName: 'settings',
|
||||
newValue: {
|
||||
...metric.settings?.settings,
|
||||
beta: e.target.value,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -69,9 +77,13 @@ export const MovingAverageSettingsEditor = ({ metric }: Props) => {
|
||||
<Input
|
||||
onBlur={(e) =>
|
||||
dispatch(
|
||||
changeMetricSetting(metric, 'settings', {
|
||||
...metric.settings?.settings,
|
||||
gamma: e.target.value,
|
||||
changeMetricSetting({
|
||||
metric,
|
||||
settingName: 'settings',
|
||||
newValue: {
|
||||
...metric.settings?.settings,
|
||||
gamma: e.target.value,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -82,9 +94,13 @@ export const MovingAverageSettingsEditor = ({ metric }: Props) => {
|
||||
<Input
|
||||
onBlur={(e) =>
|
||||
dispatch(
|
||||
changeMetricSetting(metric, 'settings', {
|
||||
...metric.settings?.settings,
|
||||
period: e.target.value!,
|
||||
changeMetricSetting({
|
||||
metric,
|
||||
settingName: 'settings',
|
||||
newValue: {
|
||||
...metric.settings?.settings,
|
||||
period: e.target.value!,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -96,7 +112,11 @@ export const MovingAverageSettingsEditor = ({ metric }: Props) => {
|
||||
<InlineSwitch
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(
|
||||
changeMetricSetting(metric, 'settings', { ...metric.settings?.settings, pad: e.target.checked })
|
||||
changeMetricSetting({
|
||||
metric,
|
||||
settingName: 'settings',
|
||||
newValue: { ...metric.settings?.settings, pad: e.target.checked },
|
||||
})
|
||||
)
|
||||
}
|
||||
checked={!!metric.settings?.settings?.pad}
|
||||
@ -109,7 +129,7 @@ export const MovingAverageSettingsEditor = ({ metric }: Props) => {
|
||||
<InlineField label="Minimize" labelWidth={16}>
|
||||
<InlineSwitch
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(changeMetricSetting(metric, 'minimize', e.target.checked))
|
||||
dispatch(changeMetricSetting({ metric, settingName: 'minimize', newValue: e.target.checked }))
|
||||
}
|
||||
checked={!!metric.settings?.minimize}
|
||||
/>
|
||||
|
@ -2,7 +2,6 @@ import React, { ComponentProps, useState } from 'react';
|
||||
import { InlineField, Input } from '@grafana/ui';
|
||||
import { useDispatch } from '../../../../hooks/useStatelessReducer';
|
||||
import { changeMetricSetting } from '../state/actions';
|
||||
import { ChangeMetricSettingAction } from '../state/types';
|
||||
import { SettingKeyOf } from '../../../types';
|
||||
import { MetricAggregationWithInlineScript, MetricAggregationWithSettings } from '../aggregations';
|
||||
import { uniqueId } from 'lodash';
|
||||
@ -23,7 +22,7 @@ export function SettingField<T extends MetricAggregationWithSettings, K extends
|
||||
placeholder,
|
||||
tooltip,
|
||||
}: Props<T, K>) {
|
||||
const dispatch = useDispatch<ChangeMetricSettingAction<T>>();
|
||||
const dispatch = useDispatch();
|
||||
const [id] = useState(uniqueId(`es-field-id-`));
|
||||
const settings = metric.settings;
|
||||
|
||||
@ -38,7 +37,7 @@ export function SettingField<T extends MetricAggregationWithSettings, K extends
|
||||
<Input
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
onBlur={(e) => dispatch(changeMetricSetting(metric, settingName, e.target.value as any))}
|
||||
onBlur={(e) => dispatch(changeMetricSetting({ metric, settingName, newValue: e.target.value }))}
|
||||
defaultValue={defaultValue}
|
||||
/>
|
||||
</InlineField>
|
||||
|
@ -26,11 +26,11 @@ export const TopMetricsSettingsEditor: FunctionComponent<Props> = ({ metric }) =
|
||||
menuShouldPortal
|
||||
onChange={(e) =>
|
||||
dispatch(
|
||||
changeMetricSetting(
|
||||
changeMetricSetting({
|
||||
metric,
|
||||
'metrics',
|
||||
e.map((v) => v.value!)
|
||||
)
|
||||
settingName: 'metrics',
|
||||
newValue: e.map((v) => v.value!),
|
||||
})
|
||||
)
|
||||
}
|
||||
loadOptions={getMetricsOptions}
|
||||
@ -42,7 +42,7 @@ export const TopMetricsSettingsEditor: FunctionComponent<Props> = ({ metric }) =
|
||||
<InlineField label="Order" labelWidth={16}>
|
||||
<Select
|
||||
menuShouldPortal
|
||||
onChange={(e) => dispatch(changeMetricSetting(metric, 'order', e.value))}
|
||||
onChange={(e) => dispatch(changeMetricSetting({ metric, settingName: 'order', newValue: e.value }))}
|
||||
options={orderOptions}
|
||||
value={metric.settings?.order}
|
||||
/>
|
||||
@ -61,7 +61,7 @@ export const TopMetricsSettingsEditor: FunctionComponent<Props> = ({ metric }) =
|
||||
margin-right: 0;
|
||||
`}
|
||||
loadOptions={getOrderByOptions}
|
||||
onChange={(e) => dispatch(changeMetricSetting(metric, 'orderBy', e.value))}
|
||||
onChange={(e) => dispatch(changeMetricSetting({ metric, settingName: 'orderBy', newValue: e.value }))}
|
||||
placeholder="Select Field"
|
||||
value={metric.settings?.orderBy}
|
||||
/>
|
||||
|
@ -78,7 +78,7 @@ export const SettingsEditor = ({ metric, previousMetrics }: Props) => {
|
||||
<InlineField label="Size" {...inlineFieldProps}>
|
||||
<Input
|
||||
id={`ES-query-${query.refId}_metric-${metric.id}-size`}
|
||||
onBlur={(e) => dispatch(changeMetricSetting(metric, 'size', e.target.value))}
|
||||
onBlur={(e) => dispatch(changeMetricSetting({ metric, settingName: 'size', newValue: e.target.value }))}
|
||||
defaultValue={metric.settings?.size ?? metricAggregationConfig['raw_data'].defaults.settings?.size}
|
||||
/>
|
||||
</InlineField>
|
||||
@ -96,7 +96,7 @@ export const SettingsEditor = ({ metric, previousMetrics }: Props) => {
|
||||
<ExtendedStatSetting
|
||||
key={stat.value}
|
||||
stat={stat}
|
||||
onChange={(checked) => dispatch(changeMetricMeta(metric, stat.value, checked))}
|
||||
onChange={(newValue) => dispatch(changeMetricMeta({ metric, meta: stat.value, newValue }))}
|
||||
value={
|
||||
metric.meta?.[stat.value] !== undefined
|
||||
? !!metric.meta?.[stat.value]
|
||||
@ -112,7 +112,15 @@ export const SettingsEditor = ({ metric, previousMetrics }: Props) => {
|
||||
{metric.type === 'percentiles' && (
|
||||
<InlineField label="Percentiles" {...inlineFieldProps}>
|
||||
<Input
|
||||
onBlur={(e) => dispatch(changeMetricSetting(metric, 'percents', e.target.value.split(',').filter(Boolean)))}
|
||||
onBlur={(e) =>
|
||||
dispatch(
|
||||
changeMetricSetting({
|
||||
metric,
|
||||
settingName: 'percents',
|
||||
newValue: e.target.value.split(',').filter(Boolean),
|
||||
})
|
||||
)
|
||||
}
|
||||
defaultValue={
|
||||
metric.settings?.percents || metricAggregationConfig['percentiles'].defaults.settings?.percents
|
||||
}
|
||||
@ -127,7 +135,7 @@ export const SettingsEditor = ({ metric, previousMetrics }: Props) => {
|
||||
<Select
|
||||
menuShouldPortal
|
||||
id={`ES-query-${query.refId}_metric-${metric.id}-unit`}
|
||||
onChange={(e) => dispatch(changeMetricSetting(metric, 'unit', e.value))}
|
||||
onChange={(e) => dispatch(changeMetricSetting({ metric, settingName: 'unit', newValue: e.value }))}
|
||||
options={rateAggUnitOptions}
|
||||
value={metric.settings?.unit}
|
||||
/>
|
||||
@ -137,7 +145,7 @@ export const SettingsEditor = ({ metric, previousMetrics }: Props) => {
|
||||
<Select
|
||||
menuShouldPortal
|
||||
id={`ES-query-${query.refId}_metric-${metric.id}-mode`}
|
||||
onChange={(e) => dispatch(changeMetricSetting(metric, 'mode', e.value))}
|
||||
onChange={(e) => dispatch(changeMetricSetting({ metric, settingName: 'mode', newValue: e.value }))}
|
||||
options={rateAggModeOptions}
|
||||
value={metric.settings?.unit}
|
||||
/>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { MetricEditor } from './MetricEditor';
|
||||
import { useDispatch } from '../../../hooks/useStatelessReducer';
|
||||
import { MetricAggregationAction } from './state/types';
|
||||
import { metricAggregationConfig } from './utils';
|
||||
import { addMetric, removeMetric, toggleMetricVisibility } from './state/actions';
|
||||
import { MetricAggregation } from './aggregations';
|
||||
@ -14,7 +13,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const MetricAggregationsEditor = ({ nextId }: Props) => {
|
||||
const dispatch = useDispatch<MetricAggregationAction>();
|
||||
const dispatch = useDispatch();
|
||||
const { metrics } = useQuery();
|
||||
const totalMetrics = metrics?.length || 0;
|
||||
|
||||
|
@ -1,96 +1,23 @@
|
||||
import { SettingKeyOf } from '../../../types';
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import { MetricAggregation, MetricAggregationWithMeta, MetricAggregationWithSettings } from '../aggregations';
|
||||
import {
|
||||
ADD_METRIC,
|
||||
CHANGE_METRIC_FIELD,
|
||||
CHANGE_METRIC_TYPE,
|
||||
REMOVE_METRIC,
|
||||
TOGGLE_METRIC_VISIBILITY,
|
||||
CHANGE_METRIC_SETTING,
|
||||
CHANGE_METRIC_META,
|
||||
CHANGE_METRIC_ATTRIBUTE,
|
||||
MetricAggregationAction,
|
||||
ChangeMetricAttributeAction,
|
||||
ChangeMetricSettingAction,
|
||||
ChangeMetricMetaAction,
|
||||
} from './types';
|
||||
|
||||
export const addMetric = (id: MetricAggregation['id']): MetricAggregationAction => ({
|
||||
type: ADD_METRIC,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
export const removeMetric = (id: MetricAggregation['id']): MetricAggregationAction => ({
|
||||
type: REMOVE_METRIC,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
export const changeMetricType = (
|
||||
id: MetricAggregation['id'],
|
||||
type: MetricAggregation['type']
|
||||
): MetricAggregationAction => ({
|
||||
type: CHANGE_METRIC_TYPE,
|
||||
payload: {
|
||||
id,
|
||||
type,
|
||||
},
|
||||
});
|
||||
|
||||
export const changeMetricField = (id: MetricAggregation['id'], field: string): MetricAggregationAction => ({
|
||||
type: CHANGE_METRIC_FIELD,
|
||||
payload: {
|
||||
id,
|
||||
field,
|
||||
},
|
||||
});
|
||||
|
||||
export const toggleMetricVisibility = (id: MetricAggregation['id']): MetricAggregationAction => ({
|
||||
type: TOGGLE_METRIC_VISIBILITY,
|
||||
payload: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
export const changeMetricAttribute = <T extends MetricAggregation, K extends Extract<keyof T, string>>(
|
||||
metric: T,
|
||||
attribute: K,
|
||||
newValue: T[K]
|
||||
): ChangeMetricAttributeAction<T> => ({
|
||||
type: CHANGE_METRIC_ATTRIBUTE,
|
||||
payload: {
|
||||
metric,
|
||||
attribute,
|
||||
newValue,
|
||||
},
|
||||
});
|
||||
|
||||
export const changeMetricSetting = <T extends MetricAggregationWithSettings, K extends SettingKeyOf<T>>(
|
||||
metric: T,
|
||||
settingName: K,
|
||||
// Maybe this could have been NonNullable<T['settings']>[K], but it doesn't seem to work really well
|
||||
newValue: NonNullable<T['settings']>[K]
|
||||
): ChangeMetricSettingAction<T> => ({
|
||||
type: CHANGE_METRIC_SETTING,
|
||||
payload: {
|
||||
metric,
|
||||
settingName,
|
||||
newValue,
|
||||
},
|
||||
});
|
||||
|
||||
export const changeMetricMeta = <T extends MetricAggregationWithMeta>(
|
||||
metric: T,
|
||||
meta: Extract<keyof Required<T>['meta'], string>,
|
||||
newValue: string | number | boolean
|
||||
): ChangeMetricMetaAction<T> => ({
|
||||
type: CHANGE_METRIC_META,
|
||||
payload: {
|
||||
metric,
|
||||
meta,
|
||||
newValue,
|
||||
},
|
||||
});
|
||||
export const addMetric = createAction<MetricAggregation['id']>('@metrics/add');
|
||||
export const removeMetric = createAction<MetricAggregation['id']>('@metrics/remove');
|
||||
export const toggleMetricVisibility = createAction<MetricAggregation['id']>('@metrics/toggle_visibility');
|
||||
export const changeMetricField = createAction<{ id: MetricAggregation['id']; field: string }>('@metrics/change_field');
|
||||
export const changeMetricType = createAction<{ id: MetricAggregation['id']; type: MetricAggregation['type'] }>(
|
||||
'@metrics/change_type'
|
||||
);
|
||||
export const changeMetricAttribute = createAction<{ metric: MetricAggregation; attribute: string; newValue: any }>(
|
||||
'@metrics/change_attr'
|
||||
);
|
||||
export const changeMetricSetting = createAction<{
|
||||
metric: MetricAggregationWithSettings;
|
||||
settingName: string;
|
||||
newValue: any;
|
||||
}>('@metrics/change_setting');
|
||||
export const changeMetricMeta = createAction<{
|
||||
metric: MetricAggregationWithMeta;
|
||||
meta: string;
|
||||
newValue: any;
|
||||
}>('@metrics/change_meta');
|
||||
|
@ -77,7 +77,7 @@ describe('Metric Aggregations Reducer', () => {
|
||||
|
||||
reducerTester<ElasticsearchQuery['metrics']>()
|
||||
.givenReducer(reducer, [firstAggregation, secondAggregation])
|
||||
.whenActionIsDispatched(changeMetricType(secondAggregation.id, expectedSecondAggregation.type))
|
||||
.whenActionIsDispatched(changeMetricType({ id: secondAggregation.id, type: expectedSecondAggregation.type }))
|
||||
.thenStateShouldEqual([firstAggregation, { ...secondAggregation, type: expectedSecondAggregation.type }]);
|
||||
});
|
||||
|
||||
@ -99,7 +99,7 @@ describe('Metric Aggregations Reducer', () => {
|
||||
|
||||
reducerTester<ElasticsearchQuery['metrics']>()
|
||||
.givenReducer(reducer, [firstAggregation, secondAggregation])
|
||||
.whenActionIsDispatched(changeMetricType(secondAggregation.id, expectedAggregation.type))
|
||||
.whenActionIsDispatched(changeMetricType({ id: secondAggregation.id, type: expectedAggregation.type }))
|
||||
.thenStateShouldEqual([expectedAggregation]);
|
||||
});
|
||||
});
|
||||
@ -128,10 +128,10 @@ describe('Metric Aggregations Reducer', () => {
|
||||
reducerTester<ElasticsearchQuery['metrics']>()
|
||||
.givenReducer(reducer, [firstAggregation, secondAggregation])
|
||||
// When changing a a pipelineAggregation field we set both pipelineAgg and field
|
||||
.whenActionIsDispatched(changeMetricField(secondAggregation.id, expectedSecondAggregation.field))
|
||||
.whenActionIsDispatched(changeMetricField({ id: secondAggregation.id, field: expectedSecondAggregation.field }))
|
||||
.thenStateShouldEqual([firstAggregation, expectedSecondAggregation])
|
||||
// otherwhise only field
|
||||
.whenActionIsDispatched(changeMetricField(firstAggregation.id, expectedFirstAggregation.field))
|
||||
.whenActionIsDispatched(changeMetricField({ id: firstAggregation.id, field: expectedFirstAggregation.field }))
|
||||
.thenStateShouldEqual([expectedFirstAggregation, expectedSecondAggregation]);
|
||||
});
|
||||
|
||||
@ -173,7 +173,9 @@ describe('Metric Aggregations Reducer', () => {
|
||||
|
||||
reducerTester<ElasticsearchQuery['metrics']>()
|
||||
.givenReducer(reducer, [firstAggregation, secondAggregation])
|
||||
.whenActionIsDispatched(changeMetricSetting(firstAggregation, 'unit', expectedSettings.unit!))
|
||||
.whenActionIsDispatched(
|
||||
changeMetricSetting({ metric: firstAggregation, settingName: 'unit', newValue: expectedSettings.unit! })
|
||||
)
|
||||
.thenStateShouldEqual([{ ...firstAggregation, settings: expectedSettings }, secondAggregation]);
|
||||
});
|
||||
|
||||
@ -196,7 +198,7 @@ describe('Metric Aggregations Reducer', () => {
|
||||
|
||||
reducerTester<ElasticsearchQuery['metrics']>()
|
||||
.givenReducer(reducer, [firstAggregation, secondAggregation])
|
||||
.whenActionIsDispatched(changeMetricMeta(firstAggregation, 'avg', expectedMeta.avg!))
|
||||
.whenActionIsDispatched(changeMetricMeta({ metric: firstAggregation, meta: 'avg', newValue: expectedMeta.avg! }))
|
||||
.thenStateShouldEqual([{ ...firstAggregation, meta: expectedMeta }, secondAggregation]);
|
||||
});
|
||||
|
||||
@ -214,7 +216,9 @@ describe('Metric Aggregations Reducer', () => {
|
||||
|
||||
reducerTester<ElasticsearchQuery['metrics']>()
|
||||
.givenReducer(reducer, [firstAggregation, secondAggregation])
|
||||
.whenActionIsDispatched(changeMetricAttribute(firstAggregation, 'hide', expectedHide))
|
||||
.whenActionIsDispatched(
|
||||
changeMetricAttribute({ metric: firstAggregation, attribute: 'hide', newValue: expectedHide })
|
||||
)
|
||||
.thenStateShouldEqual([{ ...firstAggregation, hide: expectedHide }, secondAggregation]);
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Action } from '@reduxjs/toolkit';
|
||||
import { defaultMetricAgg } from '../../../../query_def';
|
||||
import { ElasticsearchQuery } from '../../../../types';
|
||||
import { removeEmpty } from '../../../../utils';
|
||||
import { INIT, InitAction } from '../../state';
|
||||
import { initQuery } from '../../state';
|
||||
import {
|
||||
isMetricAggregationWithMeta,
|
||||
isMetricAggregationWithSettings,
|
||||
@ -10,156 +11,156 @@ import {
|
||||
} from '../aggregations';
|
||||
import { getChildren, metricAggregationConfig } from '../utils';
|
||||
import {
|
||||
ADD_METRIC,
|
||||
CHANGE_METRIC_TYPE,
|
||||
REMOVE_METRIC,
|
||||
TOGGLE_METRIC_VISIBILITY,
|
||||
MetricAggregationAction,
|
||||
CHANGE_METRIC_FIELD,
|
||||
CHANGE_METRIC_SETTING,
|
||||
CHANGE_METRIC_META,
|
||||
CHANGE_METRIC_ATTRIBUTE,
|
||||
} from './types';
|
||||
addMetric,
|
||||
changeMetricAttribute,
|
||||
changeMetricField,
|
||||
changeMetricMeta,
|
||||
changeMetricSetting,
|
||||
changeMetricType,
|
||||
removeMetric,
|
||||
toggleMetricVisibility,
|
||||
} from './actions';
|
||||
|
||||
export const reducer = (
|
||||
state: ElasticsearchQuery['metrics'],
|
||||
action: MetricAggregationAction | InitAction
|
||||
): ElasticsearchQuery['metrics'] => {
|
||||
switch (action.type) {
|
||||
case ADD_METRIC:
|
||||
return [...state!, defaultMetricAgg(action.payload.id)];
|
||||
export const reducer = (state: ElasticsearchQuery['metrics'], action: Action): ElasticsearchQuery['metrics'] => {
|
||||
if (addMetric.match(action)) {
|
||||
return [...state!, defaultMetricAgg(action.payload)];
|
||||
}
|
||||
|
||||
case REMOVE_METRIC:
|
||||
const metricToRemove = state!.find((m) => m.id === action.payload.id)!;
|
||||
const metricsToRemove = [metricToRemove, ...getChildren(metricToRemove, state!)];
|
||||
const resultingMetrics = state!.filter(
|
||||
(metric) => !metricsToRemove.some((toRemove) => toRemove.id === metric.id)
|
||||
);
|
||||
if (resultingMetrics.length === 0) {
|
||||
return [defaultMetricAgg('1')];
|
||||
if (removeMetric.match(action)) {
|
||||
const metricToRemove = state!.find((m) => m.id === action.payload)!;
|
||||
const metricsToRemove = [metricToRemove, ...getChildren(metricToRemove, state!)];
|
||||
const resultingMetrics = state!.filter((metric) => !metricsToRemove.some((toRemove) => toRemove.id === metric.id));
|
||||
if (resultingMetrics.length === 0) {
|
||||
return [defaultMetricAgg('1')];
|
||||
}
|
||||
return resultingMetrics;
|
||||
}
|
||||
|
||||
if (changeMetricType.match(action)) {
|
||||
return state!
|
||||
.filter((metric) =>
|
||||
// When the new metric type is `isSingleMetric` we remove all other metrics from the query
|
||||
// leaving only the current one.
|
||||
!!metricAggregationConfig[action.payload.type].isSingleMetric ? metric.id === action.payload.id : true
|
||||
)
|
||||
.map((metric) => {
|
||||
if (metric.id !== action.payload.id) {
|
||||
return metric;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: The previous version of the query editor was keeping some of the old metric's configurations
|
||||
in the new selected one (such as field or some settings).
|
||||
It the future would be nice to have the same behavior but it's hard without a proper definition,
|
||||
as Elasticsearch will error sometimes if some settings are not compatible.
|
||||
*/
|
||||
return {
|
||||
id: metric.id,
|
||||
type: action.payload.type,
|
||||
...metricAggregationConfig[action.payload.type].defaults,
|
||||
} as MetricAggregation;
|
||||
});
|
||||
}
|
||||
|
||||
if (changeMetricField.match(action)) {
|
||||
return state!.map((metric) => {
|
||||
if (metric.id !== action.payload.id) {
|
||||
return metric;
|
||||
}
|
||||
return resultingMetrics;
|
||||
|
||||
case CHANGE_METRIC_TYPE:
|
||||
return state!
|
||||
.filter((metric) =>
|
||||
// When the new metric type is `isSingleMetric` we remove all other metrics from the query
|
||||
// leaving only the current one.
|
||||
!!metricAggregationConfig[action.payload.type].isSingleMetric ? metric.id === action.payload.id : true
|
||||
)
|
||||
.map((metric) => {
|
||||
if (metric.id !== action.payload.id) {
|
||||
return metric;
|
||||
}
|
||||
const newMetric = {
|
||||
...metric,
|
||||
field: action.payload.field,
|
||||
};
|
||||
|
||||
/*
|
||||
TODO: The previous version of the query editor was keeping some of the old metric's configurations
|
||||
in the new selected one (such as field or some settings).
|
||||
It the future would be nice to have the same behavior but it's hard without a proper definition,
|
||||
as Elasticsearch will error sometimes if some settings are not compatible.
|
||||
*/
|
||||
return {
|
||||
id: metric.id,
|
||||
type: action.payload.type,
|
||||
...metricAggregationConfig[action.payload.type].defaults,
|
||||
} as MetricAggregation;
|
||||
if (isPipelineAggregation(metric)) {
|
||||
return { ...newMetric, pipelineAgg: action.payload.field };
|
||||
}
|
||||
|
||||
return newMetric;
|
||||
});
|
||||
}
|
||||
|
||||
if (toggleMetricVisibility.match(action)) {
|
||||
return state!.map((metric) => {
|
||||
if (metric.id !== action.payload) {
|
||||
return metric;
|
||||
}
|
||||
|
||||
return {
|
||||
...metric,
|
||||
hide: !metric.hide,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (changeMetricSetting.match(action)) {
|
||||
return state!.map((metric) => {
|
||||
if (metric.id !== action.payload.metric.id) {
|
||||
return metric;
|
||||
}
|
||||
|
||||
// TODO: Here, instead of this if statement, we should assert that metric is MetricAggregationWithSettings
|
||||
if (isMetricAggregationWithSettings(metric)) {
|
||||
const newSettings = removeEmpty({
|
||||
...metric.settings,
|
||||
[action.payload.settingName]: action.payload.newValue,
|
||||
});
|
||||
|
||||
case CHANGE_METRIC_FIELD:
|
||||
return state!.map((metric) => {
|
||||
if (metric.id !== action.payload.id) {
|
||||
return metric;
|
||||
}
|
||||
|
||||
const newMetric = {
|
||||
...metric,
|
||||
field: action.payload.field,
|
||||
};
|
||||
|
||||
if (isPipelineAggregation(metric)) {
|
||||
return { ...newMetric, pipelineAgg: action.payload.field };
|
||||
}
|
||||
|
||||
return newMetric;
|
||||
});
|
||||
|
||||
case TOGGLE_METRIC_VISIBILITY:
|
||||
return state!.map((metric) => {
|
||||
if (metric.id !== action.payload.id) {
|
||||
return metric;
|
||||
}
|
||||
|
||||
return {
|
||||
...metric,
|
||||
hide: !metric.hide,
|
||||
settings: {
|
||||
...newSettings,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
case CHANGE_METRIC_SETTING:
|
||||
return state!.map((metric) => {
|
||||
if (metric.id !== action.payload.metric.id) {
|
||||
return metric;
|
||||
}
|
||||
|
||||
// TODO: Here, instead of this if statement, we should assert that metric is MetricAggregationWithSettings
|
||||
if (isMetricAggregationWithSettings(metric)) {
|
||||
const newSettings = removeEmpty({
|
||||
...metric.settings,
|
||||
[action.payload.settingName]: action.payload.newValue,
|
||||
});
|
||||
|
||||
return {
|
||||
...metric,
|
||||
settings: {
|
||||
...newSettings,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// This should never happen.
|
||||
return metric;
|
||||
});
|
||||
|
||||
case CHANGE_METRIC_META:
|
||||
return state!.map((metric) => {
|
||||
if (metric.id !== action.payload.metric.id) {
|
||||
return metric;
|
||||
}
|
||||
|
||||
// TODO: Here, instead of this if statement, we should assert that metric is MetricAggregationWithMeta
|
||||
if (isMetricAggregationWithMeta(metric)) {
|
||||
return {
|
||||
...metric,
|
||||
meta: {
|
||||
...metric.meta,
|
||||
[action.payload.meta]: action.payload.newValue,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// This should never happen.
|
||||
return metric;
|
||||
});
|
||||
|
||||
case CHANGE_METRIC_ATTRIBUTE:
|
||||
return state!.map((metric) => {
|
||||
if (metric.id !== action.payload.metric.id) {
|
||||
return metric;
|
||||
}
|
||||
|
||||
return {
|
||||
...metric,
|
||||
[action.payload.attribute]: action.payload.newValue,
|
||||
};
|
||||
});
|
||||
|
||||
case INIT:
|
||||
if (state?.length || 0 > 0) {
|
||||
return state;
|
||||
}
|
||||
return [defaultMetricAgg('1')];
|
||||
|
||||
default:
|
||||
return state;
|
||||
// This should never happen.
|
||||
return metric;
|
||||
});
|
||||
}
|
||||
|
||||
if (changeMetricMeta.match(action)) {
|
||||
return state!.map((metric) => {
|
||||
if (metric.id !== action.payload.metric.id) {
|
||||
return metric;
|
||||
}
|
||||
|
||||
// TODO: Here, instead of this if statement, we should assert that metric is MetricAggregationWithMeta
|
||||
if (isMetricAggregationWithMeta(metric)) {
|
||||
return {
|
||||
...metric,
|
||||
meta: {
|
||||
...metric.meta,
|
||||
[action.payload.meta]: action.payload.newValue,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// This should never happen.
|
||||
return metric;
|
||||
});
|
||||
}
|
||||
|
||||
if (changeMetricAttribute.match(action)) {
|
||||
return state!.map((metric) => {
|
||||
if (metric.id !== action.payload.metric.id) {
|
||||
return metric;
|
||||
}
|
||||
|
||||
return {
|
||||
...metric,
|
||||
[action.payload.attribute]: action.payload.newValue,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (initQuery.match(action)) {
|
||||
if (state?.length || 0 > 0) {
|
||||
return state;
|
||||
}
|
||||
return [defaultMetricAgg('1')];
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
@ -1,89 +0,0 @@
|
||||
import { Action } from '../../../../hooks/useStatelessReducer';
|
||||
import { SettingKeyOf } from '../../../types';
|
||||
import {
|
||||
MetricAggregation,
|
||||
MetricAggregationWithMeta,
|
||||
MetricAggregationWithSettings,
|
||||
MetricAggregationWithField,
|
||||
} from '../aggregations';
|
||||
|
||||
export const ADD_METRIC = '@metrics/add';
|
||||
export const REMOVE_METRIC = '@metrics/remove';
|
||||
export const CHANGE_METRIC_TYPE = '@metrics/change_type';
|
||||
export const CHANGE_METRIC_FIELD = '@metrics/change_field';
|
||||
export const CHANGE_METRIC_SETTING = '@metrics/change_setting';
|
||||
export const CHANGE_METRIC_META = '@metrics/change_meta';
|
||||
export const CHANGE_METRIC_ATTRIBUTE = '@metrics/change_attr';
|
||||
export const TOGGLE_METRIC_VISIBILITY = '@metrics/toggle_visibility';
|
||||
|
||||
export interface AddMetricAction extends Action<typeof ADD_METRIC> {
|
||||
payload: {
|
||||
id: MetricAggregation['id'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface RemoveMetricAction extends Action<typeof REMOVE_METRIC> {
|
||||
payload: {
|
||||
id: MetricAggregation['id'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangeMetricTypeAction extends Action<typeof CHANGE_METRIC_TYPE> {
|
||||
payload: {
|
||||
id: MetricAggregation['id'];
|
||||
type: MetricAggregation['type'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangeMetricFieldAction extends Action<typeof CHANGE_METRIC_FIELD> {
|
||||
payload: {
|
||||
id: MetricAggregation['id'];
|
||||
field: MetricAggregationWithField['field'];
|
||||
};
|
||||
}
|
||||
export interface ToggleMetricVisibilityAction extends Action<typeof TOGGLE_METRIC_VISIBILITY> {
|
||||
payload: {
|
||||
id: MetricAggregation['id'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangeMetricSettingAction<T extends MetricAggregationWithSettings>
|
||||
extends Action<typeof CHANGE_METRIC_SETTING> {
|
||||
payload: {
|
||||
metric: T;
|
||||
settingName: SettingKeyOf<T>;
|
||||
newValue: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangeMetricMetaAction<T extends MetricAggregationWithMeta> extends Action<typeof CHANGE_METRIC_META> {
|
||||
payload: {
|
||||
metric: T;
|
||||
meta: Extract<keyof Required<T>['meta'], string>;
|
||||
newValue: string | number | boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangeMetricAttributeAction<
|
||||
T extends MetricAggregation,
|
||||
K extends Extract<keyof T, string> = Extract<keyof T, string>
|
||||
> extends Action<typeof CHANGE_METRIC_ATTRIBUTE> {
|
||||
payload: {
|
||||
metric: T;
|
||||
attribute: K;
|
||||
newValue: T[K];
|
||||
};
|
||||
}
|
||||
|
||||
type CommonActions =
|
||||
| AddMetricAction
|
||||
| RemoveMetricAction
|
||||
| ChangeMetricTypeAction
|
||||
| ChangeMetricFieldAction
|
||||
| ToggleMetricVisibilityAction;
|
||||
|
||||
export type MetricAggregationAction<T extends MetricAggregation = MetricAggregation> =
|
||||
| (T extends MetricAggregationWithSettings ? ChangeMetricSettingAction<T> : never)
|
||||
| (T extends MetricAggregationWithMeta ? ChangeMetricMetaAction<T> : never)
|
||||
| ChangeMetricAttributeAction<T>
|
||||
| CommonActions;
|
@ -1,69 +1,36 @@
|
||||
import { Action } from '../../hooks/useStatelessReducer';
|
||||
import { Action, createAction } from '@reduxjs/toolkit';
|
||||
import { ElasticsearchQuery } from '../../types';
|
||||
|
||||
export const INIT = 'init';
|
||||
const CHANGE_QUERY = 'change_query';
|
||||
const CHANGE_ALIAS_PATTERN = 'change_alias_pattern';
|
||||
|
||||
export interface InitAction extends Action<typeof INIT> {}
|
||||
|
||||
interface ChangeQueryAction extends Action<typeof CHANGE_QUERY> {
|
||||
payload: {
|
||||
query: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ChangeAliasPatternAction extends Action<typeof CHANGE_ALIAS_PATTERN> {
|
||||
payload: {
|
||||
aliasPattern: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* When the `initQuery` Action is dispatched, the query gets populated with default values where values are not present.
|
||||
* This means it won't override any existing value in place, but just ensure the query is in a "runnable" state.
|
||||
*/
|
||||
export const initQuery = (): InitAction => ({ type: INIT });
|
||||
export const initQuery = createAction('init');
|
||||
|
||||
export const changeQuery = (query: string): ChangeQueryAction => ({
|
||||
type: CHANGE_QUERY,
|
||||
payload: {
|
||||
query,
|
||||
},
|
||||
});
|
||||
export const changeQuery = createAction<ElasticsearchQuery['query']>('change_query');
|
||||
|
||||
export const changeAliasPattern = (aliasPattern: string): ChangeAliasPatternAction => ({
|
||||
type: CHANGE_ALIAS_PATTERN,
|
||||
payload: {
|
||||
aliasPattern,
|
||||
},
|
||||
});
|
||||
export const changeAliasPattern = createAction<ElasticsearchQuery['alias']>('change_alias_pattern');
|
||||
|
||||
export const queryReducer = (prevQuery: ElasticsearchQuery['query'], action: ChangeQueryAction | InitAction) => {
|
||||
switch (action.type) {
|
||||
case CHANGE_QUERY:
|
||||
return action.payload.query;
|
||||
|
||||
case INIT:
|
||||
return prevQuery || '';
|
||||
|
||||
default:
|
||||
return prevQuery;
|
||||
export const queryReducer = (prevQuery: ElasticsearchQuery['query'], action: Action) => {
|
||||
if (changeQuery.match(action)) {
|
||||
return action.payload;
|
||||
}
|
||||
|
||||
if (initQuery.match(action)) {
|
||||
return prevQuery || '';
|
||||
}
|
||||
|
||||
return prevQuery;
|
||||
};
|
||||
|
||||
export const aliasPatternReducer = (
|
||||
prevAliasPattern: ElasticsearchQuery['alias'],
|
||||
action: ChangeAliasPatternAction | InitAction
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case CHANGE_ALIAS_PATTERN:
|
||||
return action.payload.aliasPattern;
|
||||
|
||||
case INIT:
|
||||
return prevAliasPattern || '';
|
||||
|
||||
default:
|
||||
return prevAliasPattern;
|
||||
export const aliasPatternReducer = (prevAliasPattern: ElasticsearchQuery['alias'], action: Action) => {
|
||||
if (changeAliasPattern.match(action)) {
|
||||
return action.payload;
|
||||
}
|
||||
|
||||
if (initQuery.match(action)) {
|
||||
return prevAliasPattern || '';
|
||||
}
|
||||
|
||||
return prevAliasPattern;
|
||||
};
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { createContext, useCallback, useContext } from 'react';
|
||||
import { Action } from '@reduxjs/toolkit';
|
||||
|
||||
export interface Action<T extends string = string> {
|
||||
type: T;
|
||||
}
|
||||
|
||||
export type Reducer<S, A extends Action = Action> = (state: S, action: A) => S;
|
||||
export type Reducer<S, A extends Action> = (state: S, action: A) => S;
|
||||
|
||||
export const combineReducers = <S, A extends Action = Action>(reducers: { [P in keyof S]: Reducer<S[P], A> }) => (
|
||||
state: S,
|
||||
|
@ -3,7 +3,7 @@ set -e
|
||||
|
||||
echo -e "Collecting code stats (typescript errors & more)"
|
||||
|
||||
ERROR_COUNT_LIMIT=73
|
||||
ERROR_COUNT_LIMIT=61
|
||||
ERROR_COUNT="$(./node_modules/.bin/tsc --project tsconfig.json --noEmit --strict true | grep -oP 'Found \K(\d+)')"
|
||||
|
||||
if [ "$ERROR_COUNT" -gt $ERROR_COUNT_LIMIT ]; then
|
||||
|
Loading…
Reference in New Issue
Block a user