mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
060e8088ae
commit
72367cf1ad
@ -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>
|
||||
|
@ -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({
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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>
|
@ -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`);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
</>
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user