diff --git a/public/app/plugins/datasource/prometheus/components/PromExploreExtraField.tsx b/public/app/plugins/datasource/prometheus/components/PromExploreExtraField.tsx index 37dce028125..254318e4960 100644 --- a/public/app/plugins/datasource/prometheus/components/PromExploreExtraField.tsx +++ b/public/app/plugins/datasource/prometheus/components/PromExploreExtraField.tsx @@ -16,16 +16,7 @@ export interface PromExploreExtraFieldProps { export const PromExploreExtraField: React.FC = memo( ({ query, datasource, onChange, onRunQuery }) => { - const rangeOptions = [ - { value: 'range', label: 'Range', description: 'Run query over a range of time.' }, - { - value: 'instant', - label: 'Instant', - description: 'Run query against a single point in time. For this query, the "To" time is used.', - }, - { value: 'both', label: 'Both', description: 'Run an Instant query and a Range query.' }, - ]; - + const rangeOptions = getQueryTypeOptions(true); const prevQuery = usePrevious(query); const onExemplarChange = useCallback( @@ -53,17 +44,7 @@ export const PromExploreExtraField: React.FC = memo( } } - function onQueryTypeChange(queryType: string) { - let nextQuery; - if (queryType === 'instant') { - nextQuery = { ...query, instant: true, range: false }; - } else if (queryType === 'range') { - nextQuery = { ...query, instant: false, range: true }; - } else { - nextQuery = { ...query, instant: true, range: true }; - } - onChange(nextQuery); - } + const onQueryTypeChange = getQueryTypeChangeHandler(query, onChange); return (
@@ -123,6 +104,35 @@ export const PromExploreExtraField: React.FC = memo( PromExploreExtraField.displayName = 'PromExploreExtraField'; +export function getQueryTypeOptions(includeBoth: boolean) { + const rangeOptions = [ + { value: 'range', label: 'Range', description: 'Run query over a range of time' }, + { + value: 'instant', + label: 'Instant', + description: 'Run query against a single point in time. For this query, the "To" time is used', + }, + ]; + + if (includeBoth) { + rangeOptions.push({ value: 'both', label: 'Both', description: 'Run an Instant query and a Range query' }); + } + + return rangeOptions; +} + +export function getQueryTypeChangeHandler(query: PromQuery, onChange: (update: PromQuery) => void) { + return (queryType: string) => { + if (queryType === 'instant') { + onChange({ ...query, instant: true, range: false }); + } else if (queryType === 'range') { + onChange({ ...query, instant: false, range: true }); + } else { + onChange({ ...query, instant: true, range: true }); + } + }; +} + export const testIds = { extraFieldEditor: 'prom-editor-extra-field', stepField: 'prom-editor-extra-field-step', diff --git a/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx b/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx index 4301778a755..4426a77c69b 100644 --- a/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx +++ b/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx @@ -13,7 +13,7 @@ import { PromQueryEditorProps } from './types'; const { Switch } = LegacyForms; -const FORMAT_OPTIONS: Array> = [ +export const FORMAT_OPTIONS: Array> = [ { label: 'Time series', value: 'time_series' }, { label: 'Table', value: 'table' }, { label: 'Heatmap', value: 'heatmap' }, diff --git a/public/app/plugins/datasource/prometheus/querybuilder/PromQueryModeller.test.ts b/public/app/plugins/datasource/prometheus/querybuilder/PromQueryModeller.test.ts index f4fffe81d2e..649710338fb 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/PromQueryModeller.test.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/PromQueryModeller.test.ts @@ -1,4 +1,5 @@ import { PromQueryModeller } from './PromQueryModeller'; +import { PromOperationId } from './types'; describe('PromQueryModeller', () => { const modeller = new PromQueryModeller(); @@ -41,7 +42,7 @@ describe('PromQueryModeller', () => { modeller.renderQuery({ metric: 'metric', labels: [], - operations: [{ id: 'histogram_quantile', params: [0.86] }], + operations: [{ id: PromOperationId.HistogramQuantile, params: [0.86] }], }) ).toBe('histogram_quantile(0.86, metric)'); }); @@ -51,7 +52,7 @@ describe('PromQueryModeller', () => { modeller.renderQuery({ metric: 'metric', labels: [], - operations: [{ id: 'label_replace', params: ['server', '$1', 'instance', 'as(.*)d'] }], + operations: [{ id: PromOperationId.LabelReplace, params: ['server', '$1', 'instance', 'as(.*)d'] }], }) ).toBe('label_replace(metric, "server", "$1", "instance", "as(.*)d")'); }); @@ -94,7 +95,7 @@ describe('PromQueryModeller', () => { modeller.renderQuery({ metric: 'metric', labels: [{ label: 'pod', op: '=', value: 'A' }], - operations: [{ id: 'rate', params: ['auto'] }], + operations: [{ id: PromOperationId.Rate, params: ['auto'] }], }) ).toBe('rate(metric{pod="A"}[$__rate_interval])'); }); @@ -104,7 +105,7 @@ describe('PromQueryModeller', () => { modeller.renderQuery({ metric: 'metric', labels: [{ label: 'pod', op: '=', value: 'A' }], - operations: [{ id: 'increase', params: ['auto'] }], + operations: [{ id: PromOperationId.Increase, params: ['auto'] }], }) ).toBe('increase(metric{pod="A"}[$__rate_interval])'); }); @@ -114,7 +115,7 @@ describe('PromQueryModeller', () => { modeller.renderQuery({ metric: 'metric', labels: [{ label: 'pod', op: '=', value: 'A' }], - operations: [{ id: 'rate', params: ['10m'] }], + operations: [{ id: PromOperationId.Rate, params: ['10m'] }], }) ).toBe('rate(metric{pod="A"}[10m])'); }); @@ -124,7 +125,7 @@ describe('PromQueryModeller', () => { modeller.renderQuery({ metric: 'metric', labels: [], - operations: [{ id: '__multiply_by', params: [1000] }], + operations: [{ id: PromOperationId.MultiplyBy, params: [1000] }], }) ).toBe('metric * 1000'); }); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.test.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.test.tsx index 655a00dd434..c1c960626ee 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.test.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.test.tsx @@ -47,8 +47,6 @@ const bugQuery: PromVisualQuery = { describe('PromQueryBuilder', () => { it('shows empty just with metric selected', async () => { setup(); - // One should be select another query preview - expect(screen.getAllByText('random_metric').length).toBe(2); // Add label expect(screen.getByLabelText('Add')).toBeInTheDocument(); expect(screen.getByLabelText('Add operation')).toBeInTheDocument(); @@ -67,9 +65,6 @@ describe('PromQueryBuilder', () => { expect(screen.getByText('Binary operations')).toBeInTheDocument(); expect(screen.getByText('Operator')).toBeInTheDocument(); expect(screen.getByText('Vector matches')).toBeInTheDocument(); - expect(screen.getByLabelText('selector').textContent).toBe( - 'sum by(instance, job) (rate(random_metric{instance="localhost:9090"}[$__rate_interval])) / sum by(app) (metric2{foo="bar"})' - ); }); it('tries to load metrics without labels', async () => { diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.tsx index a681cc534be..9eddc271c4b 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.tsx @@ -3,12 +3,11 @@ import { MetricSelect } from './MetricSelect'; import { PromVisualQuery } from '../types'; import { LabelFilters } from '../shared/LabelFilters'; import { OperationList } from '../shared/OperationList'; -import { EditorRows, EditorRow } from '@grafana/experimental'; +import { EditorRow } from '@grafana/experimental'; import { PrometheusDatasource } from '../../datasource'; import { NestedQueryList } from './NestedQueryList'; import { promQueryModeller } from '../PromQueryModeller'; import { QueryBuilderLabelFilter } from '../shared/types'; -import { QueryPreview } from './QueryPreview'; import { DataSourceApi, SelectableValue } from '@grafana/data'; import { OperationsEditorRow } from '../shared/OperationsEditorRow'; @@ -20,7 +19,7 @@ export interface Props { nested?: boolean; } -export const PromQueryBuilder = React.memo(({ datasource, query, onChange, onRunQuery, nested }) => { +export const PromQueryBuilder = React.memo(({ datasource, query, onChange, onRunQuery }) => { const onChangeLabels = (labels: QueryBuilderLabelFilter[]) => { onChange({ ...query, labels }); }; @@ -78,7 +77,7 @@ export const PromQueryBuilder = React.memo(({ datasource, query, onChange }; return ( - + <> (({ datasource, query, onChange )} - {!nested && ( - - - - )} - + ); }); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilderOptions.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilderOptions.tsx new file mode 100644 index 00000000000..775b76c733e --- /dev/null +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilderOptions.tsx @@ -0,0 +1,119 @@ +import React, { SyntheticEvent } from 'react'; +import { EditorRow, EditorField } from '@grafana/experimental'; +import { CoreApp, SelectableValue } from '@grafana/data'; +import { Input, RadioButtonGroup, Select, Switch } from '@grafana/ui'; +import { QueryOptionGroup } from '../shared/QueryOptionGroup'; +import { PromQuery } from '../../types'; +import { FORMAT_OPTIONS } from '../../components/PromQueryEditor'; +import { getQueryTypeChangeHandler, getQueryTypeOptions } from '../../components/PromExploreExtraField'; + +export interface Props { + query: PromQuery; + app?: CoreApp; + onChange: (update: PromQuery) => void; + onRunQuery: () => void; +} + +export const PromQueryBuilderOptions = React.memo(({ query, app, onChange, onRunQuery }) => { + const formatOption = FORMAT_OPTIONS.find((option) => option.value === query.format) || FORMAT_OPTIONS[0]; + + const onChangeFormat = (value: SelectableValue) => { + onChange({ ...query, format: value.value }); + onRunQuery(); + }; + + const onLegendFormatChanged = (evt: React.FocusEvent) => { + onChange({ ...query, legendFormat: evt.currentTarget.value }); + onRunQuery(); + }; + + const onChangeStep = (evt: React.FocusEvent) => { + onChange({ ...query, interval: evt.currentTarget.value }); + onRunQuery(); + }; + + const queryTypeOptions = getQueryTypeOptions(false); + const onQueryTypeChange = getQueryTypeChangeHandler(query, onChange); + + const onExemplarChange = (event: SyntheticEvent) => { + const isEnabled = event.currentTarget.checked; + onChange({ ...query, exemplar: isEnabled }); + onRunQuery(); + }; + + const showExemplarSwitch = app !== CoreApp.UnifiedAlerting && !query.instant; + + return ( + + + + + + + An additional lower limit for the step parameter of the Prometheus query and for the{' '} + $__interval and $__rate_interval variables. + + } + > + + + + +