grafana/public/app/plugins/datasource/prometheus/querybuilder/operations.ts

377 lines
12 KiB
TypeScript

import { binaryScalarOperations } from './binaryScalarOperations';
import { LabelParamEditor } from './components/LabelParamEditor';
import {
defaultAddOperationHandler,
functionRendererLeft,
functionRendererRight,
getPromAndLokiOperationDisplayName,
getRangeVectorParamDef,
rangeRendererLeftWithParams,
rangeRendererRightWithParams,
} from './shared/operationUtils';
import {
QueryBuilderOperation,
QueryBuilderOperationDef,
QueryWithOperations,
VisualQueryModeller,
} from './shared/types';
import { PromOperationId, PromVisualQuery, PromVisualQueryOperationCategory } from './types';
export function getOperationDefinitions(): QueryBuilderOperationDef[] {
const list: QueryBuilderOperationDef[] = [
{
id: PromOperationId.HistogramQuantile,
name: 'Histogram quantile',
params: [{ name: 'Quantile', type: 'number', options: [0.99, 0.95, 0.9, 0.75, 0.5, 0.25] }],
defaultParams: [0.9],
category: PromVisualQueryOperationCategory.Functions,
renderer: functionRendererLeft,
addOperationHandler: defaultAddOperationHandler,
},
{
id: PromOperationId.LabelReplace,
name: 'Label replace',
params: [
{ name: 'Destination label', type: 'string' },
{ name: 'Replacement', type: 'string' },
{ name: 'Source label', type: 'string' },
{ name: 'Regex', type: 'string' },
],
category: PromVisualQueryOperationCategory.Functions,
defaultParams: ['', '$1', '', '(.*)'],
renderer: functionRendererRight,
addOperationHandler: defaultAddOperationHandler,
},
{
id: PromOperationId.Ln,
name: 'Ln',
params: [],
defaultParams: [],
category: PromVisualQueryOperationCategory.Functions,
renderer: functionRendererLeft,
addOperationHandler: defaultAddOperationHandler,
},
createRangeFunction(PromOperationId.Changes),
createRangeFunction(PromOperationId.Rate, true),
createRangeFunction(PromOperationId.Irate),
createRangeFunction(PromOperationId.Increase, true),
createRangeFunction(PromOperationId.Idelta),
createRangeFunction(PromOperationId.Delta),
createFunction({
id: PromOperationId.HoltWinters,
params: [
getRangeVectorParamDef(),
{ name: 'Smoothing Factor', type: 'number' },
{ name: 'Trend Factor', type: 'number' },
],
defaultParams: ['$__interval', 0.5, 0.5],
alternativesKey: 'range function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: rangeRendererRightWithParams,
addOperationHandler: addOperationWithRangeVector,
changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
}),
createFunction({
id: PromOperationId.PredictLinear,
params: [getRangeVectorParamDef(), { name: 'Seconds from now', type: 'number' }],
defaultParams: ['$__interval', 60],
alternativesKey: 'range function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: rangeRendererRightWithParams,
addOperationHandler: addOperationWithRangeVector,
changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
}),
createFunction({
id: PromOperationId.QuantileOverTime,
params: [getRangeVectorParamDef(), { name: 'Quantile', type: 'number' }],
defaultParams: ['$__interval', 0.5],
alternativesKey: 'overtime function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: rangeRendererLeftWithParams,
addOperationHandler: addOperationWithRangeVector,
changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
}),
...binaryScalarOperations,
{
id: PromOperationId.NestedQuery,
name: 'Binary operation with query',
params: [],
defaultParams: [],
category: PromVisualQueryOperationCategory.BinaryOps,
renderer: (model, def, innerExpr) => innerExpr,
addOperationHandler: addNestedQueryHandler,
},
createFunction({ id: PromOperationId.Abs }),
createFunction({ id: PromOperationId.Absent }),
createFunction({
id: PromOperationId.Acos,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({
id: PromOperationId.Acosh,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({
id: PromOperationId.Asin,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({
id: PromOperationId.Asinh,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({
id: PromOperationId.Atan,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({
id: PromOperationId.Atanh,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({ id: PromOperationId.Ceil }),
createFunction({
id: PromOperationId.Clamp,
name: 'Clamp',
params: [
{ name: 'Minimum Scalar', type: 'number' },
{ name: 'Maximum Scalar', type: 'number' },
],
defaultParams: [1, 1],
}),
createFunction({
id: PromOperationId.ClampMax,
params: [{ name: 'Maximum Scalar', type: 'number' }],
defaultParams: [1],
}),
createFunction({
id: PromOperationId.ClampMin,
params: [{ name: 'Minimum Scalar', type: 'number' }],
defaultParams: [1],
}),
createFunction({
id: PromOperationId.Cos,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({
id: PromOperationId.Cosh,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({
id: PromOperationId.DayOfMonth,
category: PromVisualQueryOperationCategory.Time,
}),
createFunction({
id: PromOperationId.DayOfWeek,
category: PromVisualQueryOperationCategory.Time,
}),
createFunction({
id: PromOperationId.DayOfYear,
category: PromVisualQueryOperationCategory.Time,
}),
createFunction({
id: PromOperationId.DaysInMonth,
category: PromVisualQueryOperationCategory.Time,
}),
createFunction({ id: PromOperationId.Deg }),
createRangeFunction(PromOperationId.Deriv),
//
createFunction({ id: PromOperationId.Exp }),
createFunction({ id: PromOperationId.Floor }),
createFunction({ id: PromOperationId.Group }),
createFunction({ id: PromOperationId.Hour }),
createFunction({
id: PromOperationId.LabelJoin,
params: [
{
name: 'Destination Label',
type: 'string',
editor: LabelParamEditor,
},
{
name: 'Separator',
type: 'string',
},
{
name: 'Source Label',
type: 'string',
restParam: true,
optional: true,
editor: LabelParamEditor,
},
],
defaultParams: ['', ',', ''],
renderer: labelJoinRenderer,
addOperationHandler: labelJoinAddOperationHandler,
}),
createFunction({ id: PromOperationId.Log10 }),
createFunction({ id: PromOperationId.Log2 }),
createFunction({ id: PromOperationId.Minute }),
createFunction({ id: PromOperationId.Month }),
createFunction({
id: PromOperationId.Pi,
renderer: (model) => `${model.id}()`,
}),
createFunction({
id: PromOperationId.Quantile,
params: [{ name: 'Value', type: 'number' }],
defaultParams: [1],
renderer: functionRendererLeft,
}),
createFunction({ id: PromOperationId.Rad }),
createRangeFunction(PromOperationId.Resets),
createFunction({
id: PromOperationId.Round,
category: PromVisualQueryOperationCategory.Functions,
params: [{ name: 'To Nearest', type: 'number' }],
defaultParams: [1],
}),
createFunction({ id: PromOperationId.Scalar }),
createFunction({ id: PromOperationId.Sgn }),
createFunction({ id: PromOperationId.Sin, category: PromVisualQueryOperationCategory.Trigonometric }),
createFunction({
id: PromOperationId.Sinh,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({ id: PromOperationId.Sort }),
createFunction({ id: PromOperationId.SortDesc }),
createFunction({ id: PromOperationId.Sqrt }),
createFunction({ id: PromOperationId.Stddev }),
createFunction({
id: PromOperationId.Tan,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({
id: PromOperationId.Tanh,
category: PromVisualQueryOperationCategory.Trigonometric,
}),
createFunction({
id: PromOperationId.Time,
renderer: (model) => `${model.id}()`,
}),
createFunction({ id: PromOperationId.Timestamp }),
createFunction({
id: PromOperationId.Vector,
params: [{ name: 'Value', type: 'number' }],
defaultParams: [1],
renderer: (model) => `${model.id}(${model.params[0]})`,
}),
createFunction({ id: PromOperationId.Year }),
];
return list;
}
export function createFunction(definition: Partial<QueryBuilderOperationDef>): QueryBuilderOperationDef {
return {
...definition,
id: definition.id!,
name: definition.name ?? getPromAndLokiOperationDisplayName(definition.id!),
params: definition.params ?? [],
defaultParams: definition.defaultParams ?? [],
category: definition.category ?? PromVisualQueryOperationCategory.Functions,
renderer: definition.renderer ?? (definition.params ? functionRendererRight : functionRendererLeft),
addOperationHandler: definition.addOperationHandler ?? defaultAddOperationHandler,
};
}
export function createRangeFunction(name: string, withRateInterval = false): QueryBuilderOperationDef {
return {
id: name,
name: getPromAndLokiOperationDisplayName(name),
params: [getRangeVectorParamDef(withRateInterval)],
defaultParams: [withRateInterval ? '$__rate_interval' : '$__interval'],
alternativesKey: 'range function',
category: PromVisualQueryOperationCategory.RangeFunctions,
renderer: operationWithRangeVectorRenderer,
addOperationHandler: addOperationWithRangeVector,
changeTypeHandler: operationTypeChangedHandlerForRangeFunction,
};
}
function operationTypeChangedHandlerForRangeFunction(
operation: QueryBuilderOperation,
newDef: QueryBuilderOperationDef
) {
// validate current parameter
if (operation.params[0] === '$__rate_interval' && newDef.defaultParams[0] !== '$__rate_interval') {
operation.params = newDef.defaultParams;
} else if (operation.params[0] === '$__interval' && newDef.defaultParams[0] !== '$__interval') {
operation.params = newDef.defaultParams;
}
return operation;
}
export function operationWithRangeVectorRenderer(
model: QueryBuilderOperation,
def: QueryBuilderOperationDef,
innerExpr: string
) {
let rangeVector = (model.params ?? [])[0] ?? '5m';
return `${def.id}(${innerExpr}[${rangeVector}])`;
}
/**
* Since there can only be one operation with range vector this will replace the current one (if one was added )
*/
export function addOperationWithRangeVector(
def: QueryBuilderOperationDef,
query: PromVisualQuery,
modeller: VisualQueryModeller
) {
const newOperation: QueryBuilderOperation = {
id: def.id,
params: def.defaultParams,
};
if (query.operations.length > 0) {
// If operation exists it has to be in the registry so no point to check if it was found
const firstOp = modeller.getOperationDef(query.operations[0].id)!;
if (firstOp.addOperationHandler === addOperationWithRangeVector) {
return {
...query,
operations: [newOperation, ...query.operations.slice(1)],
};
}
}
return {
...query,
operations: [newOperation, ...query.operations],
};
}
function addNestedQueryHandler(def: QueryBuilderOperationDef, query: PromVisualQuery): PromVisualQuery {
return {
...query,
binaryQueries: [
...(query.binaryQueries ?? []),
{
operator: '/',
query,
},
],
};
}
function labelJoinRenderer(model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string) {
if (typeof model.params[1] !== 'string') {
throw 'The separator must be a string';
}
const separator = `"${model.params[1]}"`;
return `${model.id}(${innerExpr}, "${model.params[0]}", ${separator}, "${model.params.slice(2).join(separator)}")`;
}
function labelJoinAddOperationHandler<T extends QueryWithOperations>(def: QueryBuilderOperationDef, query: T) {
const newOperation: QueryBuilderOperation = {
id: def.id,
params: def.defaultParams,
};
return {
...query,
operations: [...query.operations, newOperation],
};
}