mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: Specific code mode view that has no options, instead sharing options with builder (#45260)
* Prometheus: Adding a slimmed down code mode for new prometheus query ux * Show Both option in Explore * Prometheus: Adding query defaults handling * More tweaks * Fixing defaults and logic for when to show exemplars toggle * Fixing tooltip text * Set exemplars to false when setting query type instant * Updated test
This commit is contained in:
parent
59c5f14a59
commit
aa6cee1072
@ -124,7 +124,7 @@ export function getQueryTypeOptions(includeBoth: boolean) {
|
|||||||
export function getQueryTypeChangeHandler(query: PromQuery, onChange: (update: PromQuery) => void) {
|
export function getQueryTypeChangeHandler(query: PromQuery, onChange: (update: PromQuery) => void) {
|
||||||
return (queryType: string) => {
|
return (queryType: string) => {
|
||||||
if (queryType === 'instant') {
|
if (queryType === 'instant') {
|
||||||
onChange({ ...query, instant: true, range: false });
|
onChange({ ...query, instant: true, range: false, exemplar: false });
|
||||||
} else if (queryType === 'range') {
|
} else if (queryType === 'range') {
|
||||||
onChange({ ...query, instant: false, range: true });
|
onChange({ ...query, instant: false, range: true });
|
||||||
} else {
|
} else {
|
||||||
|
@ -19,7 +19,7 @@ export const FORMAT_OPTIONS: Array<SelectableValue<string>> = [
|
|||||||
{ label: 'Heatmap', value: 'heatmap' },
|
{ label: 'Heatmap', value: 'heatmap' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const INTERVAL_FACTOR_OPTIONS: Array<SelectableValue<number>> = map([1, 2, 3, 4, 5, 10], (value: number) => ({
|
export const INTERVAL_FACTOR_OPTIONS: Array<SelectableValue<number>> = map([1, 2, 3, 4, 5, 10], (value: number) => ({
|
||||||
value,
|
value,
|
||||||
label: '1/' + value,
|
label: '1/' + value,
|
||||||
}));
|
}));
|
||||||
|
@ -6,7 +6,6 @@ import { PromQuery } from '../../types';
|
|||||||
import { buildVisualQueryFromString } from '../parsing';
|
import { buildVisualQueryFromString } from '../parsing';
|
||||||
import { promQueryModeller } from '../PromQueryModeller';
|
import { promQueryModeller } from '../PromQueryModeller';
|
||||||
import { PromQueryBuilder } from './PromQueryBuilder';
|
import { PromQueryBuilder } from './PromQueryBuilder';
|
||||||
import { PromQueryBuilderOptions } from './PromQueryBuilderOptions';
|
|
||||||
import { QueryPreview } from './QueryPreview';
|
import { QueryPreview } from './QueryPreview';
|
||||||
import { PromVisualQuery } from '../types';
|
import { PromVisualQuery } from '../types';
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ export interface Props {
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
export function PromQueryBuilderContainer(props: Props) {
|
export function PromQueryBuilderContainer(props: Props) {
|
||||||
const { query, onChange, onRunQuery, datasource, app } = props;
|
const { query, onChange, onRunQuery, datasource } = props;
|
||||||
|
|
||||||
const visQuery = buildVisualQueryFromString(query.expr || '').query;
|
const visQuery = buildVisualQueryFromString(query.expr || '').query;
|
||||||
|
|
||||||
@ -37,7 +36,6 @@ export function PromQueryBuilderContainer(props: Props) {
|
|||||||
<>
|
<>
|
||||||
<PromQueryBuilder query={visQuery} datasource={datasource} onChange={onVisQueryChange} onRunQuery={onRunQuery} />
|
<PromQueryBuilder query={visQuery} datasource={datasource} onChange={onVisQueryChange} onRunQuery={onRunQuery} />
|
||||||
{query.editorPreview && <QueryPreview query={query.expr} />}
|
{query.editorPreview && <QueryPreview query={query.expr} />}
|
||||||
<PromQueryBuilderOptions query={query} app={app} onChange={onChange} onRunQuery={onRunQuery} />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { CoreApp, SelectableValue } from '@grafana/data';
|
|||||||
import { Input, RadioButtonGroup, Select, Switch } from '@grafana/ui';
|
import { Input, RadioButtonGroup, Select, Switch } from '@grafana/ui';
|
||||||
import { QueryOptionGroup } from '../shared/QueryOptionGroup';
|
import { QueryOptionGroup } from '../shared/QueryOptionGroup';
|
||||||
import { PromQuery } from '../../types';
|
import { PromQuery } from '../../types';
|
||||||
import { FORMAT_OPTIONS } from '../../components/PromQueryEditor';
|
import { FORMAT_OPTIONS, INTERVAL_FACTOR_OPTIONS } from '../../components/PromQueryEditor';
|
||||||
import { getQueryTypeChangeHandler, getQueryTypeOptions } from '../../components/PromExploreExtraField';
|
import { getQueryTypeChangeHandler, getQueryTypeOptions } from '../../components/PromExploreExtraField';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -32,7 +32,7 @@ export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange
|
|||||||
onRunQuery();
|
onRunQuery();
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryTypeOptions = getQueryTypeOptions(false);
|
const queryTypeOptions = getQueryTypeOptions(app === CoreApp.Explore);
|
||||||
const onQueryTypeChange = getQueryTypeChangeHandler(query, onChange);
|
const onQueryTypeChange = getQueryTypeChangeHandler(query, onChange);
|
||||||
|
|
||||||
const onExemplarChange = (event: SyntheticEvent<HTMLInputElement>) => {
|
const onExemplarChange = (event: SyntheticEvent<HTMLInputElement>) => {
|
||||||
@ -41,15 +41,17 @@ export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange
|
|||||||
onRunQuery();
|
onRunQuery();
|
||||||
};
|
};
|
||||||
|
|
||||||
const showExemplarSwitch = app !== CoreApp.UnifiedAlerting && !query.instant;
|
const onIntervalFactorChange = (value: SelectableValue<number>) => {
|
||||||
|
onChange({ ...query, intervalFactor: value.value });
|
||||||
|
onRunQuery();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditorRow>
|
<EditorRow>
|
||||||
<QueryOptionGroup title="Options" collapsedInfo={getCollapsedInfo(query, formatOption)}>
|
<QueryOptionGroup title="Options" collapsedInfo={getCollapsedInfo(query, formatOption)}>
|
||||||
<EditorField
|
<EditorField
|
||||||
label="Legend"
|
label="Legend"
|
||||||
tooltip="Controls the name of the time series, using name or pattern. For example
|
tooltip="Series name override or template. Ex. {{hostname}} will be replaced with label value for hostname."
|
||||||
{{hostname}} will be replaced with label value for the label hostname."
|
|
||||||
>
|
>
|
||||||
<Input placeholder="auto" defaultValue={query.legendFormat} onBlur={onLegendFormatChanged} />
|
<Input placeholder="auto" defaultValue={query.legendFormat} onBlur={onLegendFormatChanged} />
|
||||||
</EditorField>
|
</EditorField>
|
||||||
@ -76,22 +78,42 @@ export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange
|
|||||||
<Select value={formatOption} allowCustomValue onChange={onChangeFormat} options={FORMAT_OPTIONS} />
|
<Select value={formatOption} allowCustomValue onChange={onChangeFormat} options={FORMAT_OPTIONS} />
|
||||||
</EditorField>
|
</EditorField>
|
||||||
<EditorField label="Type">
|
<EditorField label="Type">
|
||||||
<RadioButtonGroup
|
<RadioButtonGroup options={queryTypeOptions} value={getQueryTypeValue(query)} onChange={onQueryTypeChange} />
|
||||||
options={queryTypeOptions}
|
|
||||||
value={query.range && query.instant ? 'both' : query.instant ? 'instant' : 'range'}
|
|
||||||
onChange={onQueryTypeChange}
|
|
||||||
/>
|
|
||||||
</EditorField>
|
</EditorField>
|
||||||
{showExemplarSwitch && (
|
{shouldShowExemplarSwitch(query, app) && (
|
||||||
<EditorField label="Exemplars">
|
<EditorField label="Exemplars">
|
||||||
<Switch value={query.exemplar} onChange={onExemplarChange} />
|
<Switch value={query.exemplar} onChange={onExemplarChange} />
|
||||||
</EditorField>
|
</EditorField>
|
||||||
)}
|
)}
|
||||||
|
{query.intervalFactor && query.intervalFactor > 1 && (
|
||||||
|
<EditorField label="Resolution">
|
||||||
|
<Select
|
||||||
|
aria-label="Select resolution"
|
||||||
|
menuShouldPortal
|
||||||
|
isSearchable={false}
|
||||||
|
options={INTERVAL_FACTOR_OPTIONS}
|
||||||
|
onChange={onIntervalFactorChange}
|
||||||
|
value={INTERVAL_FACTOR_OPTIONS.find((option) => option.value === query.intervalFactor)}
|
||||||
|
/>
|
||||||
|
</EditorField>
|
||||||
|
)}
|
||||||
</QueryOptionGroup>
|
</QueryOptionGroup>
|
||||||
</EditorRow>
|
</EditorRow>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function shouldShowExemplarSwitch(query: PromQuery, app?: CoreApp) {
|
||||||
|
if (app === CoreApp.UnifiedAlerting || !query.range) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQueryTypeValue(query: PromQuery) {
|
||||||
|
return query.range && query.instant ? 'both' : query.instant ? 'instant' : 'range';
|
||||||
|
}
|
||||||
|
|
||||||
function getCollapsedInfo(query: PromQuery, formatOption: SelectableValue<string>): string[] {
|
function getCollapsedInfo(query: PromQuery, formatOption: SelectableValue<string>): string[] {
|
||||||
const items: string[] = [];
|
const items: string[] = [];
|
||||||
|
|
||||||
@ -105,9 +127,7 @@ function getCollapsedInfo(query: PromQuery, formatOption: SelectableValue<string
|
|||||||
items.push(`Step ${query.interval}`);
|
items.push(`Step ${query.interval}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.instant) {
|
items.push(`Type: ${getQueryTypeValue(query)}`);
|
||||||
items.push(`Instant: true`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query.exemplar) {
|
if (query.exemplar) {
|
||||||
items.push(`Exemplars: true`);
|
items.push(`Exemplars: true`);
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { PromQueryEditorProps } from '../../components/types';
|
||||||
|
import PromQueryField from '../../components/PromQueryField';
|
||||||
|
import { testIds } from '../../components/PromQueryEditor';
|
||||||
|
|
||||||
|
export function PromQueryCodeEditor({ query, datasource, range, onRunQuery, onChange, data }: PromQueryEditorProps) {
|
||||||
|
return (
|
||||||
|
<PromQueryField
|
||||||
|
datasource={datasource}
|
||||||
|
query={query}
|
||||||
|
range={range}
|
||||||
|
onRunQuery={onRunQuery}
|
||||||
|
onChange={onChange}
|
||||||
|
history={[]}
|
||||||
|
data={data}
|
||||||
|
data-testid={testIds.editor}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -71,7 +71,6 @@ describe('PromQueryEditorSelector', () => {
|
|||||||
|
|
||||||
it('shows builder when builder mode is set', async () => {
|
it('shows builder when builder mode is set', async () => {
|
||||||
renderWithMode(QueryEditorMode.Builder);
|
renderWithMode(QueryEditorMode.Builder);
|
||||||
screen.debug(undefined, 20000);
|
|
||||||
expectBuilder();
|
expectBuilder();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,6 +85,8 @@ describe('PromQueryEditorSelector', () => {
|
|||||||
expect(onChange).toBeCalledWith({
|
expect(onChange).toBeCalledWith({
|
||||||
refId: 'A',
|
refId: 'A',
|
||||||
expr: defaultQuery.expr,
|
expr: defaultQuery.expr,
|
||||||
|
instant: false,
|
||||||
|
range: true,
|
||||||
editorMode: QueryEditorMode.Builder,
|
editorMode: QueryEditorMode.Builder,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -99,6 +100,8 @@ describe('PromQueryEditorSelector', () => {
|
|||||||
expect(onChange).toBeCalledWith({
|
expect(onChange).toBeCalledWith({
|
||||||
refId: 'A',
|
refId: 'A',
|
||||||
expr: defaultQuery.expr,
|
expr: defaultQuery.expr,
|
||||||
|
instant: false,
|
||||||
|
range: true,
|
||||||
editorMode: QueryEditorMode.Builder,
|
editorMode: QueryEditorMode.Builder,
|
||||||
editorPreview: true,
|
editorPreview: true,
|
||||||
});
|
});
|
||||||
@ -119,6 +122,8 @@ describe('PromQueryEditorSelector', () => {
|
|||||||
expect(onChange).toBeCalledWith({
|
expect(onChange).toBeCalledWith({
|
||||||
refId: 'A',
|
refId: 'A',
|
||||||
expr: defaultQuery.expr,
|
expr: defaultQuery.expr,
|
||||||
|
instant: false,
|
||||||
|
range: true,
|
||||||
editorMode: QueryEditorMode.Code,
|
editorMode: QueryEditorMode.Code,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -129,6 +134,8 @@ describe('PromQueryEditorSelector', () => {
|
|||||||
expect(onChange).toBeCalledWith({
|
expect(onChange).toBeCalledWith({
|
||||||
refId: 'A',
|
refId: 'A',
|
||||||
expr: defaultQuery.expr,
|
expr: defaultQuery.expr,
|
||||||
|
instant: false,
|
||||||
|
range: true,
|
||||||
editorMode: QueryEditorMode.Explain,
|
editorMode: QueryEditorMode.Explain,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
|
import React, { SyntheticEvent, useCallback, useState } from 'react';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { GrafanaTheme2, LoadingState } from '@grafana/data';
|
import { GrafanaTheme2, LoadingState } from '@grafana/data';
|
||||||
import { EditorHeader, EditorRows, FlexItem, InlineSelect, Space } from '@grafana/experimental';
|
import { EditorHeader, EditorRows, FlexItem, InlineSelect, Space } from '@grafana/experimental';
|
||||||
import { Button, ConfirmModal, useStyles2 } from '@grafana/ui';
|
import { Button, ConfirmModal, useStyles2 } from '@grafana/ui';
|
||||||
import React, { SyntheticEvent, useCallback, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { PromQueryEditor } from '../../components/PromQueryEditor';
|
|
||||||
import { PromQueryEditorProps } from '../../components/types';
|
import { PromQueryEditorProps } from '../../components/types';
|
||||||
import { promQueryModeller } from '../PromQueryModeller';
|
import { promQueryModeller } from '../PromQueryModeller';
|
||||||
import { QueryEditorModeToggle } from '../shared/QueryEditorModeToggle';
|
import { QueryEditorModeToggle } from '../shared/QueryEditorModeToggle';
|
||||||
@ -12,13 +10,17 @@ import { QueryHeaderSwitch } from '../shared/QueryHeaderSwitch';
|
|||||||
import { QueryEditorMode } from '../shared/types';
|
import { QueryEditorMode } from '../shared/types';
|
||||||
import { PromQueryBuilderExplained } from './PromQueryBuilderExplained';
|
import { PromQueryBuilderExplained } from './PromQueryBuilderExplained';
|
||||||
import { buildVisualQueryFromString } from '../parsing';
|
import { buildVisualQueryFromString } from '../parsing';
|
||||||
|
import { PromQueryCodeEditor } from './PromQueryCodeEditor';
|
||||||
import { PromQueryBuilderContainer } from './PromQueryBuilderContainer';
|
import { PromQueryBuilderContainer } from './PromQueryBuilderContainer';
|
||||||
|
import { PromQueryBuilderOptions } from './PromQueryBuilderOptions';
|
||||||
|
import { getQueryWithDefaults } from '../types';
|
||||||
|
|
||||||
export const PromQueryEditorSelector = React.memo<PromQueryEditorProps>((props) => {
|
export const PromQueryEditorSelector = React.memo<PromQueryEditorProps>((props) => {
|
||||||
const { query, onChange, onRunQuery, data } = props;
|
const { onChange, onRunQuery, data } = props;
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
const [parseModalOpen, setParseModalOpen] = useState(false);
|
const [parseModalOpen, setParseModalOpen] = useState(false);
|
||||||
|
const query = getQueryWithDefaults(props.query, props.app);
|
||||||
|
const editorMode = query.editorMode!;
|
||||||
|
|
||||||
const onEditorModeChange = useCallback(
|
const onEditorModeChange = useCallback(
|
||||||
(newMetricEditorMode: QueryEditorMode) => {
|
(newMetricEditorMode: QueryEditorMode) => {
|
||||||
@ -42,15 +44,6 @@ export const PromQueryEditorSelector = React.memo<PromQueryEditorProps>((props)
|
|||||||
onRunQuery();
|
onRunQuery();
|
||||||
};
|
};
|
||||||
|
|
||||||
// If no expr (ie new query) then default to builder
|
|
||||||
const editorMode = query.editorMode ?? (query.expr ? QueryEditorMode.Code : QueryEditorMode.Builder);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (query.editorMode === undefined) {
|
|
||||||
onChange({ ...query, editorMode });
|
|
||||||
}
|
|
||||||
}, [editorMode, onChange, query]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
@ -107,7 +100,7 @@ export const PromQueryEditorSelector = React.memo<PromQueryEditorProps>((props)
|
|||||||
</EditorHeader>
|
</EditorHeader>
|
||||||
<Space v={0.5} />
|
<Space v={0.5} />
|
||||||
<EditorRows>
|
<EditorRows>
|
||||||
{editorMode === QueryEditorMode.Code && <PromQueryEditor {...props} />}
|
{editorMode === QueryEditorMode.Code && <PromQueryCodeEditor {...props} />}
|
||||||
{editorMode === QueryEditorMode.Builder && (
|
{editorMode === QueryEditorMode.Builder && (
|
||||||
<PromQueryBuilderContainer
|
<PromQueryBuilderContainer
|
||||||
query={query}
|
query={query}
|
||||||
@ -117,6 +110,9 @@ export const PromQueryEditorSelector = React.memo<PromQueryEditorProps>((props)
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{editorMode === QueryEditorMode.Explain && <PromQueryBuilderExplained query={query.expr} />}
|
{editorMode === QueryEditorMode.Explain && <PromQueryBuilderExplained query={query.expr} />}
|
||||||
|
{editorMode !== QueryEditorMode.Explain && (
|
||||||
|
<PromQueryBuilderOptions query={query} app={props.app} onChange={onChange} onRunQuery={onRunQuery} />
|
||||||
|
)}
|
||||||
</EditorRows>
|
</EditorRows>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import { CoreApp } from '@grafana/data';
|
||||||
|
import { PromQuery } from '../types';
|
||||||
import { VisualQueryBinary } from './shared/LokiAndPromQueryModellerBase';
|
import { VisualQueryBinary } from './shared/LokiAndPromQueryModellerBase';
|
||||||
import { QueryBuilderLabelFilter, QueryBuilderOperation } from './shared/types';
|
import { QueryBuilderLabelFilter, QueryBuilderOperation, QueryEditorMode } from './shared/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visual query model
|
* Visual query model
|
||||||
@ -54,12 +56,35 @@ export interface PromQueryPattern {
|
|||||||
operations: QueryBuilderOperation[];
|
operations: QueryBuilderOperation[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultEmptyQuery() {
|
/**
|
||||||
const model: PromVisualQuery = {
|
* Returns query with defaults, and boolean true/false depending on change was required
|
||||||
metric: '',
|
*/
|
||||||
labels: [],
|
export function getQueryWithDefaults(query: PromQuery, app: CoreApp | undefined): PromQuery {
|
||||||
operations: [],
|
// If no expr (ie new query) then default to builder
|
||||||
};
|
let result = query;
|
||||||
|
const editorMode = query.editorMode ?? (query.expr ? QueryEditorMode.Code : QueryEditorMode.Builder);
|
||||||
|
|
||||||
return model;
|
if (result.editorMode !== editorMode) {
|
||||||
|
result = { ...result, editorMode };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.expr == null) {
|
||||||
|
result = { ...result, expr: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to range query
|
||||||
|
if (query.range == null) {
|
||||||
|
result = { ...result, range: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
// In explore we default to both instant & range
|
||||||
|
if (query.instant == null && query.range == null) {
|
||||||
|
if (app === CoreApp.Explore) {
|
||||||
|
result = { ...result, instant: true };
|
||||||
|
} else {
|
||||||
|
result = { ...result, instant: false, range: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user