Prometheus: Add classic query option to variable query editor (#74060)

* add classic query to variable query editor

* copy fixes

* update test
This commit is contained in:
Brendan O'Handley 2023-09-11 12:13:16 -04:00 committed by GitHub
parent 0f552053e9
commit 951876b465
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 103 additions and 20 deletions

View File

@ -31,13 +31,14 @@ You have the option to use several different variable types, but variables of th
Select a Prometheus data source query type and enter the required inputs:
| Query Type | Input(\* required) | Description | Used API endpoints |
| -------------- | ------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------- |
| `Label names` | `metric` | Returns a list of all label names matching the specified `metric` regex. | /api/v1/labels |
| `Label values` | `label`\*, `metric` | Returns a list of label values for the `label` in all metrics or the optional metric. | /api/v1/label/`label`/values or /api/v1/series |
| `Metrics` | `metric` | Returns a list of metrics matching the specified `metric` regex. | /api/v1/label/\_\_name\_\_/values |
| `Query result` | `query` | Returns a list of Prometheus query result for the `query`. | /api/v1/query |
| `Series query` | `metric`, `label` or both | Returns a list of time series associated with the entered data. | /api/v1/series |
| Query Type | Input(\* required) | Description | Used API endpoints |
| --------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| `Label names` | `metric` | Returns a list of all label names matching the specified `metric` regex. | /api/v1/labels |
| `Label values` | `label`\*, `metric` | Returns a list of label values for the `label` in all metrics or the optional metric. | /api/v1/label/`label`/values or /api/v1/series |
| `Metrics` | `metric` | Returns a list of metrics matching the specified `metric` regex. | /api/v1/label/\_\_name\_\_/values |
| `Query result` | `query` | Returns a list of Prometheus query result for the `query`. | /api/v1/query |
| `Series query` | `metric`, `label` or both | Returns a list of time series associated with the entered data. | /api/v1/series |
| `Classic query` | classic query string | Deprecated, classic version of variable query editor. Enter a string with the query type using a syntax like the following: `label_values(<metric>, <label>)` | all |
For details on _metric names_, _label names_, and _label values_, refer to the [Prometheus documentation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels).

View File

@ -149,6 +149,7 @@ describe('PromVariableQueryEditor', () => {
await waitFor(() => expect(screen.getByText('Metrics')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('Query result')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('Series query')).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('Classic query')).toBeInTheDocument());
});
test('Calls onChange for label_names(match) query', async () => {
@ -167,10 +168,11 @@ describe('PromVariableQueryEditor', () => {
expect(onChange).toHaveBeenCalledWith({
query: 'label_names(that)',
refId,
qryType: 0,
});
});
test('Calls onChange for label_names, label_values, metrics, and query result queries', async () => {
test('Calls onChange for label_names, label_values, metrics, query result and and classic query.', async () => {
const onChange = jest.fn();
props.query = {
@ -184,8 +186,9 @@ describe('PromVariableQueryEditor', () => {
await selectOptionInTest(screen.getByLabelText('Query type'), 'Label values');
await selectOptionInTest(screen.getByLabelText('Query type'), 'Metrics');
await selectOptionInTest(screen.getByLabelText('Query type'), 'Query result');
await selectOptionInTest(screen.getByLabelText('Query type'), 'Classic query');
expect(onChange).toHaveBeenCalledTimes(4);
expect(onChange).toHaveBeenCalledTimes(5);
});
test('Does not call onChange for series query', async () => {
@ -220,6 +223,7 @@ describe('PromVariableQueryEditor', () => {
expect(onChange).toHaveBeenCalledWith({
query: 'metrics(a)',
refId,
qryType: 2,
})
);
});
@ -230,6 +234,7 @@ describe('PromVariableQueryEditor', () => {
props.query = {
refId: 'test',
query: 'label_names()',
qryType: 0,
};
render(<PromVariableQueryEditor {...props} onChange={onChange} />);
@ -243,6 +248,7 @@ describe('PromVariableQueryEditor', () => {
expect(onChange).toHaveBeenCalledWith({
query: 'label_values(this)',
refId,
qryType: 1,
})
);
});
@ -270,6 +276,7 @@ describe('PromVariableQueryEditor', () => {
expect(onChange).toHaveBeenCalledWith({
query: 'label_values(that,this)',
refId,
qryType: 1,
})
);
});
@ -292,6 +299,7 @@ describe('PromVariableQueryEditor', () => {
expect(onChange).toHaveBeenCalledWith({
query: 'query_result(a)',
refId,
qryType: 3,
});
});
@ -313,6 +321,30 @@ describe('PromVariableQueryEditor', () => {
expect(onChange).toHaveBeenCalledWith({
query: '{a: "example"}',
refId,
qryType: 4,
});
});
test('Calls onChange for classic query onBlur', async () => {
const onChange = jest.fn();
props.query = {
refId: 'test',
qryType: 5,
query: 'label_values(instance)',
};
render(<PromVariableQueryEditor {...props} onChange={onChange} />);
const labelSelect = screen.getByLabelText('Classic Query');
await userEvent.click(labelSelect);
const functionSelect = screen.getByLabelText('Query type').parentElement!;
await userEvent.click(functionSelect);
expect(onChange).toHaveBeenCalledWith({
query: 'label_values(instance)',
refId,
qryType: 5,
});
});
});

View File

@ -26,6 +26,7 @@ export const variableOptions = [
{ label: 'Metrics', value: QueryType.MetricNames },
{ label: 'Query result', value: QueryType.VarQueryResult },
{ label: 'Series query', value: QueryType.SeriesQuery },
{ label: 'Classic query', value: QueryType.ClassicQuery },
];
export type Props = QueryEditorProps<PrometheusDatasource, PromQuery, PromOptions, PromVariableQuery>;
@ -49,6 +50,9 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource }: Props)
// seriesQuery is only a whole
const [seriesQuery, setSeriesQuery] = useState('');
// the original variable query implementation
const [classicQuery, setClassicQuery] = 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>>>([]);
@ -59,17 +63,24 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource }: Props)
if (!query) {
return;
}
// 1. Changing from standard to custom variable editor changes the string attr from expr to query
// 2. jsonnet grafana as code passes a variable as a string
const variableQuery = variableMigration(query);
setLabelNamesMatch(variableQuery.match ?? '');
setQryType(variableQuery.qryType);
setLabel(variableQuery.label ?? '');
setMetric(variableQuery.metric ?? '');
setLabelFilters(variableQuery.labelFilters ?? []);
setVarQuery(variableQuery.varQuery ?? '');
setSeriesQuery(variableQuery.seriesQuery ?? '');
if (query.qryType === QueryType.ClassicQuery) {
setQryType(query.qryType);
setClassicQuery(query.query ?? '');
} else {
// 1. Changing from standard to custom variable editor changes the string attr from expr to query
// 2. jsonnet grafana as code passes a variable as a string
const variableQuery = variableMigration(query);
setLabelNamesMatch(variableQuery.match ?? '');
setQryType(variableQuery.qryType);
setLabel(variableQuery.label ?? '');
setMetric(variableQuery.metric ?? '');
setLabelFilters(variableQuery.labelFilters ?? []);
setVarQuery(variableQuery.varQuery ?? '');
setSeriesQuery(variableQuery.seriesQuery ?? '');
setClassicQuery(variableQuery.classicQuery ?? '');
}
}, [query]);
// set the label names options for the label values var query
@ -116,6 +127,7 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource }: Props)
match: labelNamesMatch,
varQuery,
seriesQuery,
classicQuery,
refId: 'PrometheusVariableQueryEditor-VariableQuery',
};
@ -128,6 +140,7 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource }: Props)
// setting query.query property allows for update of variable definition
onChange({
query: queryString,
qryType: updatedVar.qryType,
refId,
});
};
@ -200,6 +213,10 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource }: Props)
setSeriesQuery(e.currentTarget.value);
};
const onClassicQueryChange = (e: FormEvent<HTMLInputElement>) => {
setClassicQuery(e.currentTarget.value);
};
const promVisualQuery = useCallback(() => {
return { metric: metric, labels: labelFilters, operations: [] };
}, [metric, labelFilters]);
@ -371,6 +388,35 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource }: Props)
</InlineField>
</InlineFieldRow>
)}
{qryType === QueryType.ClassicQuery && (
<InlineFieldRow>
<InlineField
label="Classic Query"
labelWidth={20}
tooltip={
<div>
The original implemetation of the Prometheus variable query editor. Enter a string with the correct
query type and parameters as described in these docs. For example, label_values(label, metric).
</div>
}
>
<Input
type="text"
aria-label="Classic Query"
placeholder="Classic Query"
value={classicQuery}
onChange={onClassicQueryChange}
onBlur={() => {
if (qryType === QueryType.ClassicQuery && classicQuery) {
onChangeWithVariableString({ qryType });
}
}}
width={100}
/>
</InlineField>
</InlineFieldRow>
)}
</>
);
};

View File

@ -123,7 +123,9 @@ export function migrateVariableEditorBackToVariableSupport(QueryVariable: PromVa
const varQuery = removeLineBreaks(QueryVariable.varQuery);
return `query_result(${varQuery})`;
case QueryType.SeriesQuery:
return '' + QueryVariable.seriesQuery;
return QueryVariable.seriesQuery ?? '';
case QueryType.ClassicQuery:
return QueryVariable.classicQuery ?? '';
}
return '';

View File

@ -183,6 +183,7 @@ export enum PromVariableQueryType {
MetricNames,
VarQueryResult,
SeriesQuery,
ClassicQuery,
}
export interface PromVariableQuery extends DataQuery {
@ -195,6 +196,7 @@ export interface PromVariableQuery extends DataQuery {
seriesQuery?: string;
labelFilters?: QueryBuilderLabelFilter[];
match?: string;
classicQuery?: string;
}
export type StandardPromVariableQuery = {