Files
grafana/public/app/plugins/datasource/prometheus/components/VariableQueryEditor.tsx
Brendan O'Handley eedcd7d5b1 Prometheus Datasource: Improve Prom query variable editor (#58292)
* add prom query var editor with tests and migrations

* fix migration, now query not expr

* fix label_values migration

* remove comments

* fix label_values() variables order

* update UI and use more clear language

* fix tests

* use null coalescing operators

* allow users to query label values with label and metric if they have not set there flavor and version

* use enums instead of numbers for readability

* fix label&metrics switch

* update type in qv editor

* reuse datasource function to get all label names, getLabelNames(), prev named getTagKeys()

* use getLabelNames in the query var editor

* make label_values() variables label and metric more readable in the migration

* fix tooltip for label_values to remove API reference

* clean up tooltips and allow newlines in query_result function

* change function wording and exprType to query type/qryType for readability

* update prometheus query variable docs

* Update public/app/plugins/datasource/prometheus/components/VariableQueryEditor.tsx

Co-authored-by: Galen Kistler <109082771+gtk-grafana@users.noreply.github.com>

---------

Co-authored-by: Galen Kistler <109082771+gtk-grafana@users.noreply.github.com>
2023-02-09 15:35:36 -05:00

258 lines
8.3 KiB
TypeScript

import React, { FC, FormEvent, useEffect, useState } from 'react';
import { QueryEditorProps, SelectableValue } from '@grafana/data';
import { InlineField, InlineFieldRow, Input, Select, TextArea } from '@grafana/ui';
import { PrometheusDatasource } from '../datasource';
import {
migrateVariableEditorBackToVariableSupport,
migrateVariableQueryToEditor,
} from '../migrations/variableMigration';
import { PromOptions, PromQuery, PromVariableQuery, PromVariableQueryType as QueryType } from '../types';
export const variableOptions = [
{ label: 'Label names', value: QueryType.LabelNames },
{ label: 'Label values', value: QueryType.LabelValues },
{ label: 'Metrics', value: QueryType.MetricNames },
{ label: 'Query result', value: QueryType.VarQueryResult },
{ label: 'Series query', value: QueryType.SeriesQuery },
];
export type Props = QueryEditorProps<PrometheusDatasource, PromQuery, PromOptions, PromVariableQuery>;
const refId = 'PrometheusVariableQueryEditor-VariableQuery';
export const PromVariableQueryEditor: FC<Props> = ({ onChange, query, datasource }) => {
// to select the query type, i.e. label_names, label_values, etc.
const [qryType, setQryType] = useState<number | undefined>(undefined);
// list of variables for each function
const [label, setLabel] = useState('');
// metric is used for both label_values() and metric()
// label_values() metric requires a whole/complete metric
// metric() is expected to be a part of a metric string
const [metric, setMetric] = useState('');
// varQuery is a whole query, can include math/rates/etc
const [varQuery, setVarQuery] = useState('');
// seriesQuery is only a whole
const [seriesQuery, setSeriesQuery] = useState('');
// list of label names for label_values(), /api/v1/labels, contains the same results as label_names() function
const [labelOptions, setLabelOptions] = useState<Array<SelectableValue<string>>>([]);
useEffect(() => {
if (!query) {
return;
}
// Changing from standard to custom variable editor changes the string attr from expr to query
const variableQuery = query.query ? migrateVariableQueryToEditor(query.query) : query;
setQryType(variableQuery.qryType);
setLabel(variableQuery.label ?? '');
setMetric(variableQuery.metric ?? '');
setVarQuery(variableQuery.varQuery ?? '');
setSeriesQuery(variableQuery.seriesQuery ?? '');
// set the migrated label in the label options
if (variableQuery.label) {
setLabelOptions([{ label: variableQuery.label, value: variableQuery.label }]);
}
}, [query]);
// set the label names options for the label values var query
useEffect(() => {
if (qryType !== QueryType.LabelValues) {
return;
}
datasource.getLabelNames().then((labelNames: Array<{ text: string }>) => {
setLabelOptions(labelNames.map(({ text }) => ({ label: text, value: text })));
});
}, [datasource, qryType]);
const onChangeWithVariableString = (qryType: QueryType) => {
const queryVar = {
qryType: qryType,
label,
metric,
varQuery,
seriesQuery,
refId: 'PrometheusVariableQueryEditor-VariableQuery',
};
const queryString = migrateVariableEditorBackToVariableSupport(queryVar);
onChange({
query: queryString,
refId,
});
};
const onQueryTypeChange = (newType: SelectableValue<QueryType>) => {
setQryType(newType.value);
if (newType.value === QueryType.LabelNames) {
onChangeWithVariableString(newType.value);
}
};
const onLabelChange = (newLabel: SelectableValue<string>) => {
setLabel(newLabel.value ?? '');
};
const onMetricChange = (e: FormEvent<HTMLInputElement>) => {
setMetric(e.currentTarget.value);
};
const onVarQueryChange = (e: FormEvent<HTMLTextAreaElement>) => {
setVarQuery(e.currentTarget.value);
};
const onSeriesQueryChange = (e: FormEvent<HTMLInputElement>) => {
setSeriesQuery(e.currentTarget.value);
};
const handleBlur = () => {
if (qryType === QueryType.LabelNames) {
onChangeWithVariableString(qryType);
} else if (qryType === QueryType.LabelValues && label) {
onChangeWithVariableString(qryType);
} else if (qryType === QueryType.MetricNames && metric) {
onChangeWithVariableString(qryType);
} else if (qryType === QueryType.VarQueryResult && varQuery) {
onChangeWithVariableString(qryType);
} else if (qryType === QueryType.SeriesQuery && seriesQuery) {
onChangeWithVariableString(qryType);
}
};
return (
<InlineFieldRow>
<InlineField
label="Query Type"
labelWidth={20}
tooltip={
<div>The Prometheus data source plugin provides the following query types for template variables.</div>
}
>
<Select
placeholder="Select query type"
aria-label="Query type"
onChange={onQueryTypeChange}
onBlur={handleBlur}
value={qryType}
options={variableOptions}
width={25}
/>
</InlineField>
{qryType === QueryType.LabelValues && (
<>
<InlineField
label="Label"
labelWidth={20}
required
tooltip={
<div>
Returns a list of label values for the label name in all metrics unless the metric is specified.
</div>
}
>
<Select
aria-label="label-select"
onChange={onLabelChange}
onBlur={handleBlur}
value={label}
options={labelOptions}
width={25}
allowCustomValue
/>
</InlineField>
<InlineField
label="Metric"
labelWidth={20}
tooltip={<div>Optional: returns a list of label values for the label name in the specified metric.</div>}
>
<Input
type="text"
aria-label="Metric selector"
placeholder="Optional metric selector"
value={metric}
onChange={onMetricChange}
onBlur={handleBlur}
width={25}
/>
</InlineField>
</>
)}
{qryType === QueryType.MetricNames && (
<>
<InlineField
label="Metric Regex"
labelWidth={20}
tooltip={<div>Returns a list of metrics matching the specified metric regex.</div>}
>
<Input
type="text"
aria-label="Metric selector"
placeholder="Metric Regex"
value={metric}
onChange={onMetricChange}
onBlur={handleBlur}
width={25}
/>
</InlineField>
</>
)}
{qryType === QueryType.VarQueryResult && (
<>
<InlineField
label="Query"
labelWidth={20}
tooltip={
<div>
Returns a list of Prometheus query results for the query. This can include Prometheus functions, i.e.
sum(go_goroutines).
</div>
}
>
<TextArea
type="text"
aria-label="Prometheus Query"
placeholder="Prometheus Query"
value={varQuery}
onChange={onVarQueryChange}
onBlur={handleBlur}
cols={100}
/>
</InlineField>
</>
)}
{qryType === QueryType.SeriesQuery && (
<>
<InlineField
label="Series Query"
labelWidth={20}
tooltip={
<div>
Enter enter a metric with labels, only a metric or only labels, i.e.
go_goroutines&#123;instance=&quot;localhost:9090&quot;&#125;, go_goroutines, or
&#123;instance=&quot;localhost:9090&quot;&#125;. Returns a list of time series associated with the
entered data.
</div>
}
>
<Input
type="text"
aria-label="Series Query"
placeholder="Series Query"
value={seriesQuery}
onChange={onSeriesQueryChange}
onBlur={handleBlur}
width={100}
/>
</InlineField>
</>
)}
</InlineFieldRow>
);
};