mirror of
https://github.com/grafana/grafana.git
synced 2024-11-26 02:40:26 -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) {
|
||||
return (queryType: string) => {
|
||||
if (queryType === 'instant') {
|
||||
onChange({ ...query, instant: true, range: false });
|
||||
onChange({ ...query, instant: true, range: false, exemplar: false });
|
||||
} else if (queryType === 'range') {
|
||||
onChange({ ...query, instant: false, range: true });
|
||||
} else {
|
||||
|
@ -19,7 +19,7 @@ export const FORMAT_OPTIONS: Array<SelectableValue<string>> = [
|
||||
{ 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,
|
||||
label: '1/' + value,
|
||||
}));
|
||||
|
@ -6,7 +6,6 @@ import { PromQuery } from '../../types';
|
||||
import { buildVisualQueryFromString } from '../parsing';
|
||||
import { promQueryModeller } from '../PromQueryModeller';
|
||||
import { PromQueryBuilder } from './PromQueryBuilder';
|
||||
import { PromQueryBuilderOptions } from './PromQueryBuilderOptions';
|
||||
import { QueryPreview } from './QueryPreview';
|
||||
import { PromVisualQuery } from '../types';
|
||||
|
||||
@ -24,7 +23,7 @@ export interface Props {
|
||||
* @constructor
|
||||
*/
|
||||
export function PromQueryBuilderContainer(props: Props) {
|
||||
const { query, onChange, onRunQuery, datasource, app } = props;
|
||||
const { query, onChange, onRunQuery, datasource } = props;
|
||||
|
||||
const visQuery = buildVisualQueryFromString(query.expr || '').query;
|
||||
|
||||
@ -37,7 +36,6 @@ export function PromQueryBuilderContainer(props: Props) {
|
||||
<>
|
||||
<PromQueryBuilder query={visQuery} datasource={datasource} onChange={onVisQueryChange} onRunQuery={onRunQuery} />
|
||||
{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 { QueryOptionGroup } from '../shared/QueryOptionGroup';
|
||||
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';
|
||||
|
||||
export interface Props {
|
||||
@ -32,7 +32,7 @@ export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange
|
||||
onRunQuery();
|
||||
};
|
||||
|
||||
const queryTypeOptions = getQueryTypeOptions(false);
|
||||
const queryTypeOptions = getQueryTypeOptions(app === CoreApp.Explore);
|
||||
const onQueryTypeChange = getQueryTypeChangeHandler(query, onChange);
|
||||
|
||||
const onExemplarChange = (event: SyntheticEvent<HTMLInputElement>) => {
|
||||
@ -41,15 +41,17 @@ export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange
|
||||
onRunQuery();
|
||||
};
|
||||
|
||||
const showExemplarSwitch = app !== CoreApp.UnifiedAlerting && !query.instant;
|
||||
const onIntervalFactorChange = (value: SelectableValue<number>) => {
|
||||
onChange({ ...query, intervalFactor: value.value });
|
||||
onRunQuery();
|
||||
};
|
||||
|
||||
return (
|
||||
<EditorRow>
|
||||
<QueryOptionGroup title="Options" collapsedInfo={getCollapsedInfo(query, formatOption)}>
|
||||
<EditorField
|
||||
label="Legend"
|
||||
tooltip="Controls the name of the time series, using name or pattern. For example
|
||||
{{hostname}} will be replaced with label value for the label hostname."
|
||||
tooltip="Series name override or template. Ex. {{hostname}} will be replaced with label value for hostname."
|
||||
>
|
||||
<Input placeholder="auto" defaultValue={query.legendFormat} onBlur={onLegendFormatChanged} />
|
||||
</EditorField>
|
||||
@ -76,22 +78,42 @@ export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange
|
||||
<Select value={formatOption} allowCustomValue onChange={onChangeFormat} options={FORMAT_OPTIONS} />
|
||||
</EditorField>
|
||||
<EditorField label="Type">
|
||||
<RadioButtonGroup
|
||||
options={queryTypeOptions}
|
||||
value={query.range && query.instant ? 'both' : query.instant ? 'instant' : 'range'}
|
||||
onChange={onQueryTypeChange}
|
||||
/>
|
||||
<RadioButtonGroup options={queryTypeOptions} value={getQueryTypeValue(query)} onChange={onQueryTypeChange} />
|
||||
</EditorField>
|
||||
{showExemplarSwitch && (
|
||||
{shouldShowExemplarSwitch(query, app) && (
|
||||
<EditorField label="Exemplars">
|
||||
<Switch value={query.exemplar} onChange={onExemplarChange} />
|
||||
</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>
|
||||
</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[] {
|
||||
const items: string[] = [];
|
||||
|
||||
@ -105,9 +127,7 @@ function getCollapsedInfo(query: PromQuery, formatOption: SelectableValue<string
|
||||
items.push(`Step ${query.interval}`);
|
||||
}
|
||||
|
||||
if (query.instant) {
|
||||
items.push(`Instant: true`);
|
||||
}
|
||||
items.push(`Type: ${getQueryTypeValue(query)}`);
|
||||
|
||||
if (query.exemplar) {
|
||||
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 () => {
|
||||
renderWithMode(QueryEditorMode.Builder);
|
||||
screen.debug(undefined, 20000);
|
||||
expectBuilder();
|
||||
});
|
||||
|
||||
@ -86,6 +85,8 @@ describe('PromQueryEditorSelector', () => {
|
||||
expect(onChange).toBeCalledWith({
|
||||
refId: 'A',
|
||||
expr: defaultQuery.expr,
|
||||
instant: false,
|
||||
range: true,
|
||||
editorMode: QueryEditorMode.Builder,
|
||||
});
|
||||
});
|
||||
@ -99,6 +100,8 @@ describe('PromQueryEditorSelector', () => {
|
||||
expect(onChange).toBeCalledWith({
|
||||
refId: 'A',
|
||||
expr: defaultQuery.expr,
|
||||
instant: false,
|
||||
range: true,
|
||||
editorMode: QueryEditorMode.Builder,
|
||||
editorPreview: true,
|
||||
});
|
||||
@ -119,6 +122,8 @@ describe('PromQueryEditorSelector', () => {
|
||||
expect(onChange).toBeCalledWith({
|
||||
refId: 'A',
|
||||
expr: defaultQuery.expr,
|
||||
instant: false,
|
||||
range: true,
|
||||
editorMode: QueryEditorMode.Code,
|
||||
});
|
||||
});
|
||||
@ -129,6 +134,8 @@ describe('PromQueryEditorSelector', () => {
|
||||
expect(onChange).toBeCalledWith({
|
||||
refId: 'A',
|
||||
expr: defaultQuery.expr,
|
||||
instant: false,
|
||||
range: true,
|
||||
editorMode: QueryEditorMode.Explain,
|
||||
});
|
||||
});
|
||||
|
@ -1,10 +1,8 @@
|
||||
import React, { SyntheticEvent, useCallback, useState } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import { GrafanaTheme2, LoadingState } from '@grafana/data';
|
||||
import { EditorHeader, EditorRows, FlexItem, InlineSelect, Space } from '@grafana/experimental';
|
||||
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 { promQueryModeller } from '../PromQueryModeller';
|
||||
import { QueryEditorModeToggle } from '../shared/QueryEditorModeToggle';
|
||||
@ -12,13 +10,17 @@ import { QueryHeaderSwitch } from '../shared/QueryHeaderSwitch';
|
||||
import { QueryEditorMode } from '../shared/types';
|
||||
import { PromQueryBuilderExplained } from './PromQueryBuilderExplained';
|
||||
import { buildVisualQueryFromString } from '../parsing';
|
||||
import { PromQueryCodeEditor } from './PromQueryCodeEditor';
|
||||
import { PromQueryBuilderContainer } from './PromQueryBuilderContainer';
|
||||
import { PromQueryBuilderOptions } from './PromQueryBuilderOptions';
|
||||
import { getQueryWithDefaults } from '../types';
|
||||
|
||||
export const PromQueryEditorSelector = React.memo<PromQueryEditorProps>((props) => {
|
||||
const { query, onChange, onRunQuery, data } = props;
|
||||
const { onChange, onRunQuery, data } = props;
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const [parseModalOpen, setParseModalOpen] = useState(false);
|
||||
const query = getQueryWithDefaults(props.query, props.app);
|
||||
const editorMode = query.editorMode!;
|
||||
|
||||
const onEditorModeChange = useCallback(
|
||||
(newMetricEditorMode: QueryEditorMode) => {
|
||||
@ -42,15 +44,6 @@ export const PromQueryEditorSelector = React.memo<PromQueryEditorProps>((props)
|
||||
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 (
|
||||
<>
|
||||
<ConfirmModal
|
||||
@ -107,7 +100,7 @@ export const PromQueryEditorSelector = React.memo<PromQueryEditorProps>((props)
|
||||
</EditorHeader>
|
||||
<Space v={0.5} />
|
||||
<EditorRows>
|
||||
{editorMode === QueryEditorMode.Code && <PromQueryEditor {...props} />}
|
||||
{editorMode === QueryEditorMode.Code && <PromQueryCodeEditor {...props} />}
|
||||
{editorMode === QueryEditorMode.Builder && (
|
||||
<PromQueryBuilderContainer
|
||||
query={query}
|
||||
@ -117,6 +110,9 @@ export const PromQueryEditorSelector = React.memo<PromQueryEditorProps>((props)
|
||||
/>
|
||||
)}
|
||||
{editorMode === QueryEditorMode.Explain && <PromQueryBuilderExplained query={query.expr} />}
|
||||
{editorMode !== QueryEditorMode.Explain && (
|
||||
<PromQueryBuilderOptions query={query} app={props.app} onChange={onChange} onRunQuery={onRunQuery} />
|
||||
)}
|
||||
</EditorRows>
|
||||
</>
|
||||
);
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { CoreApp } from '@grafana/data';
|
||||
import { PromQuery } from '../types';
|
||||
import { VisualQueryBinary } from './shared/LokiAndPromQueryModellerBase';
|
||||
import { QueryBuilderLabelFilter, QueryBuilderOperation } from './shared/types';
|
||||
import { QueryBuilderLabelFilter, QueryBuilderOperation, QueryEditorMode } from './shared/types';
|
||||
|
||||
/**
|
||||
* Visual query model
|
||||
@ -54,12 +56,35 @@ export interface PromQueryPattern {
|
||||
operations: QueryBuilderOperation[];
|
||||
}
|
||||
|
||||
export function getDefaultEmptyQuery() {
|
||||
const model: PromVisualQuery = {
|
||||
metric: '',
|
||||
labels: [],
|
||||
operations: [],
|
||||
};
|
||||
/**
|
||||
* Returns query with defaults, and boolean true/false depending on change was required
|
||||
*/
|
||||
export function getQueryWithDefaults(query: PromQuery, app: CoreApp | undefined): PromQuery {
|
||||
// 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