mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: Allow boolean params on operations and add bool modifier parsing (#46448)
* Allow boolean params on operations and add bool modifier parsing * Allow float number literals
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { QueryBuilderOperation, QueryBuilderOperationDef } from './shared/types';
|
||||
import { QueryBuilderOperation, QueryBuilderOperationDef, QueryBuilderOperationParamDef } from './shared/types';
|
||||
import { PromOperationId, PromVisualQueryOperationCategory } from './types';
|
||||
import { defaultAddOperationHandler } from './shared/operationUtils';
|
||||
|
||||
@@ -37,42 +37,55 @@ export const binaryScalarDefs = [
|
||||
id: PromOperationId.EqualTo,
|
||||
name: 'Equal to',
|
||||
sign: '==',
|
||||
comparison: true,
|
||||
},
|
||||
{
|
||||
id: PromOperationId.NotEqualTo,
|
||||
name: 'Not equal to',
|
||||
sign: '!=',
|
||||
comparison: true,
|
||||
},
|
||||
{
|
||||
id: PromOperationId.GreaterThan,
|
||||
name: 'Greater than',
|
||||
sign: '>',
|
||||
comparison: true,
|
||||
},
|
||||
{
|
||||
id: PromOperationId.LessThan,
|
||||
name: 'Less than',
|
||||
sign: '<',
|
||||
comparison: true,
|
||||
},
|
||||
{
|
||||
id: PromOperationId.GreaterOrEqual,
|
||||
name: 'Greater or equal to',
|
||||
sign: '>=',
|
||||
comparison: true,
|
||||
},
|
||||
{
|
||||
id: PromOperationId.LessOrEqual,
|
||||
name: 'Less or equal to',
|
||||
sign: '<=',
|
||||
comparison: true,
|
||||
},
|
||||
];
|
||||
|
||||
// Not sure about this one. It could also be a more generic 'Simple math operation' where user specifies
|
||||
// both the operator and the operand in a single input
|
||||
export const binaryScalarOperations = binaryScalarDefs.map((opDef) => {
|
||||
export const binaryScalarOperations: QueryBuilderOperationDef[] = binaryScalarDefs.map((opDef) => {
|
||||
const params: QueryBuilderOperationParamDef[] = [{ name: 'Value', type: 'number' }];
|
||||
const defaultParams: any[] = [2];
|
||||
if (opDef.comparison) {
|
||||
params.unshift({ name: 'Bool', type: 'boolean' });
|
||||
defaultParams.unshift(false);
|
||||
}
|
||||
|
||||
return {
|
||||
id: opDef.id,
|
||||
name: opDef.name,
|
||||
params: [{ name: 'Value', type: 'number' }],
|
||||
defaultParams: [2],
|
||||
params,
|
||||
defaultParams,
|
||||
alternativesKey: 'binary scalar operations',
|
||||
category: PromVisualQueryOperationCategory.BinaryOps,
|
||||
renderer: getSimpleBinaryRenderer(opDef.sign),
|
||||
@@ -82,6 +95,13 @@ export const binaryScalarOperations = binaryScalarDefs.map((opDef) => {
|
||||
|
||||
function getSimpleBinaryRenderer(operator: string) {
|
||||
return function binaryRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) {
|
||||
return `${innerExpr} ${operator} ${model.params[0]}`;
|
||||
let param = model.params[0];
|
||||
let bool = '';
|
||||
if (model.params.length === 2) {
|
||||
param = model.params[1];
|
||||
bool = model.params[0] ? ' bool' : '';
|
||||
}
|
||||
|
||||
return `${innerExpr} ${operator}${bool} ${param}`;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -414,7 +414,7 @@ describe('buildVisualQueryFromString', () => {
|
||||
});
|
||||
|
||||
it('handles scalar comparison operators', () => {
|
||||
expect(buildVisualQueryFromString('cluster_namespace_slug_dialer_name <= 2')).toEqual({
|
||||
expect(buildVisualQueryFromString('cluster_namespace_slug_dialer_name <= 2.5')).toEqual({
|
||||
errors: [],
|
||||
query: {
|
||||
metric: 'cluster_namespace_slug_dialer_name',
|
||||
@@ -422,7 +422,23 @@ describe('buildVisualQueryFromString', () => {
|
||||
operations: [
|
||||
{
|
||||
id: '__less_or_equal',
|
||||
params: [2],
|
||||
params: [false, 2.5],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('handles bool with comparison operator', () => {
|
||||
expect(buildVisualQueryFromString('cluster_namespace_slug_dialer_name <= bool 2')).toEqual({
|
||||
errors: [],
|
||||
query: {
|
||||
metric: 'cluster_namespace_slug_dialer_name',
|
||||
labels: [],
|
||||
operations: [
|
||||
{
|
||||
id: '__less_or_equal',
|
||||
params: [true, 2],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -324,9 +324,12 @@ function updateFunctionArgs(expr: string, node: SyntaxNode | null, context: Cont
|
||||
}
|
||||
|
||||
const operatorToOpName = binaryScalarDefs.reduce((acc, def) => {
|
||||
acc[def.sign] = def.id;
|
||||
acc[def.sign] = {
|
||||
id: def.id,
|
||||
comparison: def.comparison,
|
||||
};
|
||||
return acc;
|
||||
}, {} as Record<string, string>);
|
||||
}, {} as Record<string, { id: string; comparison?: boolean }>);
|
||||
|
||||
/**
|
||||
* Right now binary expressions can be represented in 2 way in visual query. As additional operation in case it is
|
||||
@@ -339,10 +342,10 @@ function handleBinary(expr: string, node: SyntaxNode, context: Context) {
|
||||
const visQuery = context.query;
|
||||
const left = node.firstChild!;
|
||||
const op = getString(expr, left.nextSibling);
|
||||
// TODO: we are skipping BinModifiers
|
||||
const binModifier = node.getChild('BinModifiers')?.getChild('Bool');
|
||||
const right = node.lastChild!;
|
||||
|
||||
const opName = operatorToOpName[op];
|
||||
const opDef = operatorToOpName[op];
|
||||
|
||||
const leftNumber = left.getChild('NumberLiteral');
|
||||
const rightNumber = right.getChild('NumberLiteral');
|
||||
@@ -359,7 +362,7 @@ function handleBinary(expr: string, node: SyntaxNode, context: Context) {
|
||||
if (rightNumber) {
|
||||
// TODO: this should be already handled in case parent is binary expression as it has to be added to parent
|
||||
// if query starts with a number that isn't handled now.
|
||||
visQuery.operations.push({ id: opName, params: [parseInt(getString(expr, right), 10)] });
|
||||
visQuery.operations.push(makeBinOp(opDef, expr, right, binModifier));
|
||||
} else {
|
||||
handleExpression(expr, right, context);
|
||||
}
|
||||
@@ -378,7 +381,7 @@ function handleBinary(expr: string, node: SyntaxNode, context: Context) {
|
||||
// is a factor for a current binary operation. So we have to add it as an operation now.
|
||||
const leftMostChild = getLeftMostChild(right);
|
||||
if (leftMostChild?.name === 'NumberLiteral') {
|
||||
visQuery.operations.push({ id: opName, params: [parseInt(getString(expr, leftMostChild), 10)] });
|
||||
visQuery.operations.push(makeBinOp(opDef, expr, leftMostChild, binModifier));
|
||||
}
|
||||
// If we added the first number literal as operation here we still can continue and handle the rest as the first
|
||||
// number will be just skipped.
|
||||
@@ -404,6 +407,22 @@ function handleBinary(expr: string, node: SyntaxNode, context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
function makeBinOp(
|
||||
opDef: { id: string; comparison?: boolean },
|
||||
expr: string,
|
||||
numberNode: SyntaxNode,
|
||||
binModifier?: SyntaxNode | null
|
||||
) {
|
||||
const params: any[] = [parseFloat(getString(expr, numberNode))];
|
||||
if (opDef.comparison) {
|
||||
params.unshift(Boolean(binModifier));
|
||||
}
|
||||
return {
|
||||
id: opDef.id,
|
||||
params,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual string of the expression. That is not stored in the tree so we have to get the indexes from the node
|
||||
* and then based on that get it from the expression.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SelectableValue, toOption } from '@grafana/data';
|
||||
import { Select } from '@grafana/ui';
|
||||
import { Checkbox, Select } from '@grafana/ui';
|
||||
import React, { ComponentType } from 'react';
|
||||
import { QueryBuilderOperationParamDef, QueryBuilderOperationParamEditorProps } from '../shared/types';
|
||||
import { AutoSizeInput } from './AutoSizeInput';
|
||||
@@ -16,14 +16,21 @@ export function getOperationParamEditor(
|
||||
return SelectInputParamEditor;
|
||||
}
|
||||
|
||||
return SimpleInputParamEditor;
|
||||
switch (paramDef.type) {
|
||||
case 'boolean':
|
||||
return BoolInputParamEditor;
|
||||
case 'number':
|
||||
case 'string':
|
||||
default:
|
||||
return SimpleInputParamEditor;
|
||||
}
|
||||
}
|
||||
|
||||
function SimpleInputParamEditor(props: QueryBuilderOperationParamEditorProps) {
|
||||
return (
|
||||
<AutoSizeInput
|
||||
id={getOperationParamId(props.operationIndex, props.index)}
|
||||
defaultValue={props.value}
|
||||
defaultValue={props.value?.toString()}
|
||||
minWidth={props.paramDef.minWidth}
|
||||
placeholder={props.paramDef.placeholder}
|
||||
title={props.paramDef.description}
|
||||
@@ -34,6 +41,16 @@ function SimpleInputParamEditor(props: QueryBuilderOperationParamEditorProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function BoolInputParamEditor(props: QueryBuilderOperationParamEditorProps) {
|
||||
return (
|
||||
<Checkbox
|
||||
id={getOperationParamId(props.operationIndex, props.index)}
|
||||
value={props.value as boolean}
|
||||
onChange={(evt) => props.onChange(props.index, evt.currentTarget.checked)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SelectInputParamEditor({
|
||||
paramDef,
|
||||
value,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
QueryBuilderOperationParamDef,
|
||||
QueryWithOperations,
|
||||
} from './types';
|
||||
import { SelectableValue } from '@grafana/data/src';
|
||||
|
||||
export function functionRendererLeft(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) {
|
||||
const params = renderParams(model, def, innerExpr);
|
||||
@@ -116,7 +117,7 @@ export function getOperationParamId(operationIndex: number, paramIndex: number)
|
||||
}
|
||||
|
||||
export function getRangeVectorParamDef(withRateInterval = false): QueryBuilderOperationParamDef {
|
||||
const param = {
|
||||
const param: QueryBuilderOperationParamDef = {
|
||||
name: 'Range',
|
||||
type: 'string',
|
||||
options: [
|
||||
@@ -134,7 +135,7 @@ export function getRangeVectorParamDef(withRateInterval = false): QueryBuilderOp
|
||||
};
|
||||
|
||||
if (withRateInterval) {
|
||||
param.options.unshift({
|
||||
(param.options as Array<SelectableValue<string>>).unshift({
|
||||
label: '$__rate_interval',
|
||||
value: '$__rate_interval',
|
||||
// tooltip: 'Always above 4x scrape interval',
|
||||
|
||||
@@ -52,11 +52,11 @@ export type QueryBuilderOperationRenderer = (
|
||||
innerExpr: string
|
||||
) => string;
|
||||
|
||||
export type QueryBuilderOperationParamValue = string | number;
|
||||
export type QueryBuilderOperationParamValue = string | number | boolean;
|
||||
|
||||
export interface QueryBuilderOperationParamDef {
|
||||
name: string;
|
||||
type: string;
|
||||
type: 'string' | 'number' | 'boolean';
|
||||
options?: string[] | number[] | Array<SelectableValue<string>>;
|
||||
hideName?: boolean;
|
||||
restParam?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user