Elasticsearch: Add interval type selector to interval field (#76805)

* Elasticsearch: add interval type selector

* Interval type: add tooltip

* DateHistogramSettingsEditor: create unit test

* Elastic histogram settings: refactor showing calendar type

* Update test

* Prettier

* DateHistogramSettingsEditor: change tooltip according to interval type

* Calendar intervals: add comment

* Prettier
This commit is contained in:
Matias Chomicki 2023-10-30 15:30:25 +01:00 committed by GitHub
parent 41d394c594
commit e5f92c010d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 119 additions and 7 deletions

View File

@ -26,6 +26,9 @@ import {
} from './types';
import { convertOrderByToMetricId, getScriptValue } from './utils';
// Omitting 1m, 1h, 1d for now, as these cover the main use cases for calendar_interval
export const calendarIntervals: string[] = ['1w', '1M', '1q', '1y'];
export class ElasticQueryBuilder {
timeField: string;
@ -100,7 +103,6 @@ export class ElasticQueryBuilder {
getDateHistogramAgg(aggDef: DateHistogram) {
const esAgg: any = {};
const settings = aggDef.settings || {};
const calendarIntervals: string[] = ['1w', '1M', '1q', '1y'];
esAgg.field = aggDef.field || this.timeField;
esAgg.min_doc_count = settings.min_doc_count || 0;

View File

@ -0,0 +1,87 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
import { DateHistogram } from 'app/plugins/datasource/elasticsearch/types';
import { useDispatch } from '../../../../hooks/useStatelessReducer';
import { DateHistogramSettingsEditor } from './DateHistogramSettingsEditor';
jest.mock('../../../../hooks/useStatelessReducer');
describe('DateHistogramSettingsEditor', () => {
test('Renders the date histogram selector', async () => {
const bucketAgg: DateHistogram = {
field: '@timestamp',
id: '2',
settings: { interval: 'auto' },
type: 'date_histogram',
};
render(<DateHistogramSettingsEditor bucketAgg={bucketAgg} />);
expect(await screen.findByText('Fixed interval')).toBeInTheDocument();
expect(await screen.findByText('auto')).toBeInTheDocument();
});
test('Renders the date histogram selector with a fixed interval', async () => {
const bucketAgg: DateHistogram = {
field: '@timestamp',
id: '2',
settings: { interval: '10s' },
type: 'date_histogram',
};
render(<DateHistogramSettingsEditor bucketAgg={bucketAgg} />);
expect(await screen.findByText('Fixed interval')).toBeInTheDocument();
expect(await screen.findByText('10s')).toBeInTheDocument();
});
test('Renders the date histogram selector with a calendar interval', async () => {
const bucketAgg: DateHistogram = {
field: '@timestamp',
id: '2',
settings: { interval: '1w' },
type: 'date_histogram',
};
render(<DateHistogramSettingsEditor bucketAgg={bucketAgg} />);
expect(await screen.findByText('Calendar interval')).toBeInTheDocument();
expect(await screen.findByText('1w')).toBeInTheDocument();
});
describe('Handling change', () => {
let dispatch = jest.fn();
beforeEach(() => {
dispatch.mockClear();
jest.mocked(useDispatch).mockReturnValue(dispatch);
});
test('Handles changing from calendar to fixed interval type', async () => {
const bucketAgg: DateHistogram = {
field: '@timestamp',
id: '2',
settings: { interval: '1w' },
type: 'date_histogram',
};
render(<DateHistogramSettingsEditor bucketAgg={bucketAgg} />);
expect(await screen.findByText('Calendar interval')).toBeInTheDocument();
expect(await screen.findByText('1w')).toBeInTheDocument();
await selectOptionInTest(screen.getByLabelText('Calendar interval'), '10s');
expect(dispatch).toHaveBeenCalledTimes(1);
});
test('Renders the date histogram selector with a calendar interval', async () => {
const bucketAgg: DateHistogram = {
field: '@timestamp',
id: '2',
settings: { interval: '1m' },
type: 'date_histogram',
};
render(<DateHistogramSettingsEditor bucketAgg={bucketAgg} />);
expect(await screen.findByText('Fixed interval')).toBeInTheDocument();
expect(await screen.findByText('1m')).toBeInTheDocument();
await selectOptionInTest(screen.getByLabelText('Fixed interval'), '1q');
expect(dispatch).toHaveBeenCalledTimes(1);
});
});
});

View File

@ -1,9 +1,10 @@
import { uniqueId } from 'lodash';
import React, { useRef } from 'react';
import React, { useCallback, useRef } from 'react';
import { GroupBase, OptionsOrGroups } from 'react-select';
import { InternalTimeZones, SelectableValue } from '@grafana/data';
import { InlineField, Input, Select, TimeZonePicker } from '@grafana/ui';
import { calendarIntervals } from 'app/plugins/datasource/elasticsearch/QueryBuilder';
import { useDispatch } from '../../../../hooks/useStatelessReducer';
import { DateHistogram } from '../../../../types';
@ -22,6 +23,10 @@ const defaultIntervalOptions: Array<SelectableValue<string>> = [
{ label: '20m', value: '20m' },
{ label: '1h', value: '1h' },
{ label: '1d', value: '1d' },
{ label: '1w', value: '1w' },
{ label: '1M', value: '1M' },
{ label: '1q', value: '1q' },
{ label: '1y', value: '1y' },
];
const hasValue =
@ -48,16 +53,35 @@ interface Props {
bucketAgg: DateHistogram;
}
const getIntervalType = (interval: string | undefined): 'calendar' | 'fixed' => {
return interval && calendarIntervals.includes(interval) ? 'calendar' : 'fixed';
};
export const DateHistogramSettingsEditor = ({ bucketAgg }: Props) => {
const dispatch = useDispatch();
const { current: baseId } = useRef(uniqueId('es-date_histogram-'));
const handleIntervalChange = ({ value }: SelectableValue<string>) =>
dispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'interval', newValue: value }));
const handleIntervalChange = useCallback(
({ value }: SelectableValue<string>) =>
dispatch(changeBucketAggregationSetting({ bucketAgg, settingName: 'interval', newValue: value })),
[bucketAgg, dispatch]
);
const intervalType = getIntervalType(
bucketAgg.settings?.interval || bucketAggregationConfig.date_histogram.defaultSettings?.interval
);
return (
<>
<InlineField label="Interval" {...inlineFieldProps}>
<InlineField
label={intervalType === 'calendar' ? 'Calendar interval' : 'Fixed interval'}
tooltip={
intervalType === 'calendar'
? 'Calendar-aware intervals adapt to varying day lengths, month durations, and leap seconds, considering the calendar context.'
: 'Fixed intervals remain constant, always being multiples of SI units, independent of calendar changes.'
}
{...inlineFieldProps}
>
<Select
inputId={uniqueId('es-date_histogram-interval')}
isValidNewOption={isValidNewOption}
@ -69,7 +93,6 @@ export const DateHistogramSettingsEditor = ({ bucketAgg }: Props) => {
})}
/>
</InlineField>
<InlineField label="Min Doc Count" {...inlineFieldProps}>
<Input
id={`${baseId}-min_doc_count`}

View File

@ -15,7 +15,7 @@ import { TermsSettingsEditor } from './TermsSettingsEditor';
import { useDescription } from './useDescription';
export const inlineFieldProps: Partial<ComponentProps<typeof InlineField>> = {
labelWidth: 16,
labelWidth: 18,
};
interface Props {