Prometheus: Cleanup annotation editor (#49615)

* Remove unused code

* Remove test

* Remove Builder mode and simplify the code

* Fix step mapping

* Fix import

* change placeholder
This commit is contained in:
Andrej Ocenas 2022-05-31 11:50:23 +02:00 committed by GitHub
parent 060e8088ae
commit 72367cf1ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 101 additions and 207 deletions

View File

@ -99,15 +99,7 @@ export const LokiQueryEditorSelector = React.memo<LokiQueryEditorProps>((props)
>
Run query
</Button>
<QueryEditorModeToggle
mode={editorMode!}
onChange={onEditorModeChange}
uiOptions={{
[QueryEditorMode.Explain]: true,
[QueryEditorMode.Code]: true,
[QueryEditorMode.Builder]: true,
}}
/>
<QueryEditorModeToggle mode={editorMode!} onChange={onEditorModeChange} />
</EditorHeader>
<Space v={0.5} />
<EditorRows>

View File

@ -1,11 +1,10 @@
import React from 'react';
import { AnnotationQuery } from '@grafana/data';
import { EditorRow, EditorField, EditorSwitch, Space } from '@grafana/experimental';
import { Input } from '@grafana/ui';
import { EditorRow, EditorField, EditorSwitch, Space, EditorRows } from '@grafana/experimental';
import { Input, AutoSizeInput } from '@grafana/ui';
import { PromQueryEditorSelector } from '../querybuilder/components/PromQueryEditorSelector';
import { QueryEditorMode } from '../querybuilder/shared/types';
import { PromQueryCodeEditor } from '../querybuilder/components/PromQueryCodeEditor';
import { PromQuery } from '../types';
import { PromQueryEditorProps } from './types';
@ -19,35 +18,47 @@ export function AnnotationQueryEditor(props: Props) {
// This is because of problematic typing. See AnnotationQueryEditorProps in grafana-data/annotations.ts.
const annotation = props.annotation!;
const onAnnotationChange = props.onAnnotationChange!;
const query = { expr: annotation.expr, refId: annotation.name, interval: annotation.step };
return (
<>
<PromQueryEditorSelector
{...props}
query={{ expr: annotation.expr, refId: annotation.name, interval: annotation.step }}
onChange={(query) =>
onAnnotationChange({
...annotation,
expr: query.expr,
step: query.interval,
})
}
uiOptions={{
modes: {
[QueryEditorMode.Explain]: false,
[QueryEditorMode.Code]: true,
[QueryEditorMode.Builder]: true,
},
runQueryButton: false,
options: {
exemplars: false,
type: false,
format: false,
minStep: true,
legend: false,
resolution: false,
},
}}
/>
<EditorRows>
<PromQueryCodeEditor
{...props}
query={query}
onChange={(query) => {
onAnnotationChange({
...annotation,
expr: query.expr,
});
}}
/>
<EditorRow>
<EditorField
label="Min step"
tooltip={
<>
An additional lower limit for the step parameter of the Prometheus query and for the{' '}
<code>$__interval</code> and <code>$__rate_interval</code> variables.
</>
}
>
<AutoSizeInput
type="text"
aria-label="Set lower limit for the step parameter"
placeholder={'auto'}
minWidth={10}
onCommitChange={(ev) => {
onAnnotationChange({
...annotation,
step: ev.currentTarget.value,
});
}}
defaultValue={query.interval}
/>
</EditorField>
</EditorRow>
</EditorRows>
<Space v={0.5} />
<EditorRow>
<EditorField
@ -58,7 +69,7 @@ export function AnnotationQueryEditor(props: Props) {
>
<Input
type="text"
placeholder="alertname"
placeholder="{{alertname}}"
value={annotation.titleFormat}
onChange={(event) => {
onAnnotationChange({
@ -89,7 +100,7 @@ export function AnnotationQueryEditor(props: Props) {
>
<Input
type="text"
placeholder="instance"
placeholder="{{instance}}"
value={annotation.textFormat}
onChange={(event) => {
onAnnotationChange({

View File

@ -62,7 +62,7 @@ import {
} from './types';
import { PrometheusVariableSupport } from './variables';
export const ANNOTATION_QUERY_STEP_DEFAULT = '60s';
const ANNOTATION_QUERY_STEP_DEFAULT = '60s';
const GET_AND_POST_METADATA_ENDPOINTS = ['api/v1/query', 'api/v1/query_range', 'api/v1/series', 'api/v1/labels'];
export class PrometheusDatasource

View File

@ -1,13 +1,7 @@
import { ANNOTATION_QUERY_STEP_DEFAULT } from './datasource';
import { plugin as PrometheusDatasourcePlugin } from './module';
describe('module', () => {
it('should have metrics query field in panels and Explore', () => {
expect(PrometheusDatasourcePlugin.components.QueryEditor).toBeDefined();
});
it('should have stepDefaultValuePlaceholder set in annotations ctrl', () => {
expect(PrometheusDatasourcePlugin.components.AnnotationsQueryCtrl).toBeDefined();
const annotationsCtrl = new PrometheusDatasourcePlugin.components.AnnotationsQueryCtrl();
expect(annotationsCtrl.stepDefaultValuePlaceholder).toEqual(ANNOTATION_QUERY_STEP_DEFAULT);
});
});

View File

@ -3,15 +3,9 @@ import { DataSourcePlugin } from '@grafana/data';
import PromCheatSheet from './components/PromCheatSheet';
import PromQueryEditorByApp from './components/PromQueryEditorByApp';
import { ConfigEditor } from './configuration/ConfigEditor';
import { ANNOTATION_QUERY_STEP_DEFAULT, PrometheusDatasource } from './datasource';
class PrometheusAnnotationsQueryCtrl {
static templateUrl = 'partials/annotations.editor.html';
stepDefaultValuePlaceholder = ANNOTATION_QUERY_STEP_DEFAULT;
}
import { PrometheusDatasource } from './datasource';
export const plugin = new DataSourcePlugin(PrometheusDatasource)
.setQueryEditor(PromQueryEditorByApp)
.setConfigEditor(ConfigEditor)
.setAnnotationQueryCtrl(PrometheusAnnotationsQueryCtrl)
.setQueryEditorHelp(PromCheatSheet);

View File

@ -1,39 +0,0 @@
<div class="gf-form-group">
<div class="gf-form">
<span class="gf-form-label width-10">Search expression</span>
<input type="text" class="gf-form-input" ng-model='ctrl.annotation.expr' placeholder="ALERTS"></input>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Step</span>
<input type="text" class="gf-form-input max-width-6" ng-model='ctrl.annotation.step' placeholder="{{::ctrl.stepDefaultValuePlaceholder}}"></input>
</div>
</div>
<div class="gf-form-group">
<h5 class="section-heading">Field formats<tip>For title and text fields, use either the name or a pattern. For example, {{instance}} is replaced with label value for the label instance.</tip></h5>
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-5">Title</span>
<input type="text" class="gf-form-input max-width-9" ng-model='ctrl.annotation.titleFormat' placeholder="alertname"></input>
</div>
<div class="gf-form">
<span class="gf-form-label width-5">Tags</span>
<input type="text" class="gf-form-input max-width-9" ng-model='ctrl.annotation.tagKeys' placeholder="label1,label2"></input>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-5">Text</span>
<input type="text" class="gf-form-input max-width-9" ng-model='ctrl.annotation.textFormat' placeholder="instance"></input>
</div>
</div>
</div>
<h5 class="section-heading">Other options</h5>
<div class="gf-form-inline">
<div class="gf-form">
<gf-form-switch class="gf-form" label="Series value as timestamp" label-class="width-14" checked="ctrl.annotation.useValueForTime"
tooltip="The unit of timestamp is milliseconds. If the unit of the series value is seconds, multiply its range vector by 1000.">
</gf-form-switch>
</div>
</div>
</div>

View File

@ -25,10 +25,9 @@ export interface Props {
app?: CoreApp;
onChange: (update: PromQuery) => void;
onRunQuery: () => void;
uiOptions: UIOptions;
}
export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange, onRunQuery, uiOptions }) => {
export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange, onRunQuery }) => {
const onChangeFormat = (value: SelectableValue<string>) => {
onChange({ ...query, format: value.value });
onRunQuery();
@ -59,53 +58,42 @@ export const PromQueryBuilderOptions = React.memo<Props>(({ query, app, onChange
return (
<EditorRow>
<QueryOptionGroup
title="Options"
collapsedInfo={getCollapsedInfo(query, formatOption.label!, queryTypeLabel, uiOptions)}
>
{uiOptions.legend && (
<PromQueryLegendEditor
legendFormat={query.legendFormat}
onChange={(legendFormat) => onChange({ ...query, legendFormat })}
onRunQuery={onRunQuery}
<QueryOptionGroup title="Options" collapsedInfo={getCollapsedInfo(query, formatOption.label!, queryTypeLabel)}>
<PromQueryLegendEditor
legendFormat={query.legendFormat}
onChange={(legendFormat) => onChange({ ...query, legendFormat })}
onRunQuery={onRunQuery}
/>
<EditorField
label="Min step"
tooltip={
<>
An additional lower limit for the step parameter of the Prometheus query and for the{' '}
<code>$__interval</code> and <code>$__rate_interval</code> variables.
</>
}
>
<AutoSizeInput
type="text"
aria-label="Set lower limit for the step parameter"
placeholder={'auto'}
minWidth={10}
onCommitChange={onChangeStep}
defaultValue={query.interval}
/>
)}
{uiOptions.minStep && (
<EditorField
label="Min step"
tooltip={
<>
An additional lower limit for the step parameter of the Prometheus query and for the{' '}
<code>$__interval</code> and <code>$__rate_interval</code> variables.
</>
}
>
<AutoSizeInput
type="text"
aria-label="Set lower limit for the step parameter"
placeholder={'auto'}
minWidth={10}
onCommitChange={onChangeStep}
defaultValue={query.interval}
/>
</EditorField>
)}
{uiOptions.format && (
<EditorField label="Format">
<Select value={formatOption} allowCustomValue onChange={onChangeFormat} options={FORMAT_OPTIONS} />
</EditorField>
)}
{uiOptions.type && (
<EditorField label="Type">
<RadioButtonGroup options={queryTypeOptions} value={queryTypeValue} onChange={onQueryTypeChange} />
</EditorField>
)}
{uiOptions.exemplars && shouldShowExemplarSwitch(query, app) && (
</EditorField>
<EditorField label="Format">
<Select value={formatOption} allowCustomValue onChange={onChangeFormat} options={FORMAT_OPTIONS} />
</EditorField>
<EditorField label="Type">
<RadioButtonGroup options={queryTypeOptions} value={queryTypeValue} onChange={onQueryTypeChange} />
</EditorField>
{shouldShowExemplarSwitch(query, app) && (
<EditorField label="Exemplars">
<EditorSwitch value={query.exemplar} onChange={onExemplarChange} />
</EditorField>
)}
{uiOptions.resolution && query.intervalFactor && query.intervalFactor > 1 && (
{query.intervalFactor && query.intervalFactor > 1 && (
<EditorField label="Resolution">
<Select
aria-label="Select resolution"
@ -133,23 +121,15 @@ function getQueryTypeValue(query: PromQuery) {
return query.range && query.instant ? 'both' : query.instant ? 'instant' : 'range';
}
function getCollapsedInfo(query: PromQuery, formatOption: string, queryType: string, uiOptions: UIOptions): string[] {
function getCollapsedInfo(query: PromQuery, formatOption: string, queryType: string): string[] {
const items: string[] = [];
if (uiOptions.legend) {
items.push(`Legend: ${getLegendModeLabel(query.legendFormat)}`);
}
if (uiOptions.format) {
items.push(`Format: ${formatOption}`);
}
if (uiOptions.minStep && query.interval) {
items.push(`Step ${query.interval}`);
}
if (uiOptions.type) {
items.push(`Type: ${queryType}`);
}
items.push(`Legend: ${getLegendModeLabel(query.legendFormat)}`);
items.push(`Format: ${formatOption}`);
items.push(`Step ${query.interval}`);
items.push(`Type: ${queryType}`);
if (uiOptions.exemplars && query.exemplar) {
if (query.exemplar) {
items.push(`Exemplars: true`);
}

View File

@ -10,43 +10,19 @@ import { PromQuery } from '../../types';
import { promQueryModeller } from '../PromQueryModeller';
import { buildVisualQueryFromString } from '../parsing';
import { FeedbackLink } from '../shared/FeedbackLink';
import { QueryEditorModeToggle, UIOptions as ModeToggleUIOptions } from '../shared/QueryEditorModeToggle';
import { QueryEditorModeToggle } from '../shared/QueryEditorModeToggle';
import { QueryHeaderSwitch } from '../shared/QueryHeaderSwitch';
import { QueryEditorMode } from '../shared/types';
import { changeEditorMode, getQueryWithDefaults } from '../state';
import { PromQueryBuilderContainer } from './PromQueryBuilderContainer';
import { PromQueryBuilderExplained } from './PromQueryBuilderExplained';
import { PromQueryBuilderOptions, UIOptions as OptionsUIOptions } from './PromQueryBuilderOptions';
import { PromQueryBuilderOptions } from './PromQueryBuilderOptions';
import { PromQueryCodeEditor } from './PromQueryCodeEditor';
interface UIOptions {
modes: ModeToggleUIOptions;
runQueryButton: boolean;
options: OptionsUIOptions;
}
const defaultOptions: UIOptions = {
modes: {
[QueryEditorMode.Explain]: true,
[QueryEditorMode.Code]: true,
[QueryEditorMode.Builder]: true,
},
runQueryButton: true,
options: {
exemplars: true,
type: true,
format: true,
minStep: true,
legend: true,
resolution: true,
},
};
type Props = PromQueryEditorProps & { uiOptions?: UIOptions };
type Props = PromQueryEditorProps;
export const PromQueryEditorSelector = React.memo<Props>((props) => {
const uiOptions = props.uiOptions || defaultOptions;
const { onChange, onRunQuery, data, app } = props;
const [parseModalOpen, setParseModalOpen] = useState(false);
const [dataIsStale, setDataIsStale] = useState(false);
@ -130,18 +106,16 @@ export const PromQueryEditorSelector = React.memo<Props>((props) => {
<FeedbackLink feedbackUrl="https://github.com/grafana/grafana/discussions/47693" />
)}
<FlexItem grow={1} />
{uiOptions.runQueryButton && (
<Button
variant={dataIsStale ? 'primary' : 'secondary'}
size="sm"
onClick={onRunQuery}
icon={data?.state === LoadingState.Loading ? 'fa fa-spinner' : undefined}
disabled={data?.state === LoadingState.Loading}
>
Run query
</Button>
)}
<QueryEditorModeToggle mode={editorMode} onChange={onEditorModeChange} uiOptions={uiOptions.modes} />
<Button
variant={dataIsStale ? 'primary' : 'secondary'}
size="sm"
onClick={onRunQuery}
icon={data?.state === LoadingState.Loading ? 'fa fa-spinner' : undefined}
disabled={data?.state === LoadingState.Loading}
>
Run query
</Button>
<QueryEditorModeToggle mode={editorMode} onChange={onEditorModeChange} />
</EditorHeader>
<Space v={0.5} />
<EditorRows>
@ -157,13 +131,7 @@ export const PromQueryEditorSelector = React.memo<Props>((props) => {
)}
{editorMode === QueryEditorMode.Explain && <PromQueryBuilderExplained query={query.expr} />}
{editorMode !== QueryEditorMode.Explain && (
<PromQueryBuilderOptions
query={query}
app={props.app}
onChange={onChange}
onRunQuery={onRunQuery}
uiOptions={uiOptions.options}
/>
<PromQueryBuilderOptions query={query} app={props.app} onChange={onChange} onRunQuery={onRunQuery} />
)}
</EditorRows>
</>

View File

@ -5,14 +5,9 @@ import { RadioButtonGroup, Tag } from '@grafana/ui';
import { QueryEditorMode } from './types';
export type UIOptions = {
[key in QueryEditorMode]: boolean;
};
export interface Props {
mode: QueryEditorMode;
onChange: (mode: QueryEditorMode) => void;
uiOptions: UIOptions;
}
const editorModes = [
@ -35,11 +30,10 @@ const editorModes = [
{ label: 'Code', value: QueryEditorMode.Code },
];
export function QueryEditorModeToggle({ mode, onChange, uiOptions }: Props) {
const modes = editorModes.filter((m) => uiOptions[m.value]);
export function QueryEditorModeToggle({ mode, onChange }: Props) {
return (
<div data-testid={'QueryEditorModeToggle'}>
<RadioButtonGroup options={modes} size="sm" value={mode} onChange={onChange} />
<RadioButtonGroup options={editorModes} size="sm" value={mode} onChange={onChange} />
</div>
);
}