From f7291ce270b01029c12a989ba8f27dcb6d15071d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 25 Feb 2022 11:18:08 +0100 Subject: [PATCH] Prometheus: Remove the auto range vector option (#45715) * Prometheus: Remove auto range option * Prometheus: Remove auto range option * Overhaul of range vector operations and default param * Make sure label is string --- .../loki/querybuilder/operations.ts | 15 +-- .../querybuilder/PromQueryModeller.test.ts | 14 +- .../prometheus/querybuilder/aggregations.ts | 21 +-- .../prometheus/querybuilder/operations.ts | 125 +++++++++--------- .../querybuilder/shared/OperationName.tsx | 7 +- .../shared/OperationParamEditor.tsx | 18 ++- .../querybuilder/shared/operationUtils.ts | 43 +++++- .../prometheus/querybuilder/shared/types.ts | 1 + 8 files changed, 132 insertions(+), 112 deletions(-) diff --git a/public/app/plugins/datasource/loki/querybuilder/operations.ts b/public/app/plugins/datasource/loki/querybuilder/operations.ts index 06e6c7cd499..8efb0c4868a 100644 --- a/public/app/plugins/datasource/loki/querybuilder/operations.ts +++ b/public/app/plugins/datasource/loki/querybuilder/operations.ts @@ -134,7 +134,7 @@ function createRangeOperation(name: string): QueryBuilderOperationDef { id: name, name: getPromAndLokiOperationDisplayName(name), params: [getRangeVectorParamDef()], - defaultParams: ['auto'], + defaultParams: ['$__interval'], alternativesKey: 'range function', category: LokiVisualQueryOperationCategory.RangeFunctions, renderer: operationWithRangeVectorRenderer, @@ -142,7 +142,7 @@ function createRangeOperation(name: string): QueryBuilderOperationDef { explainHandler: (op, def) => { let opDocs = FUNCTIONS.find((x) => x.insertText === op.id)?.documentation ?? ''; - if (op.params[0] === 'auto' || op.params[0] === '$__interval') { + if (op.params[0] === '$__interval') { return `${opDocs} \`$__interval\` is variable that will be replaced with a calculated interval based on **Max data points**, **Min interval** and query time range. You find these options you find under **Query options** at the right of the data source select dropdown.`; } else { return `${opDocs} The [range vector](https://grafana.com/docs/loki/latest/logql/metric_queries/#range-vector-aggregation) is set to \`${op.params[0]}\`.`; @@ -170,9 +170,9 @@ function createAggregationOperation(name: string): QueryBuilderOperationDef { function getRangeVectorParamDef(): QueryBuilderOperationParamDef { return { - name: 'Range vector', + name: 'Range', type: 'string', - options: ['auto', '$__interval', '$__range', '1m', '5m', '10m', '1h', '24h'], + options: ['$__interval', '$__range', '1m', '5m', '10m', '1h', '24h'], }; } @@ -181,12 +181,7 @@ function operationWithRangeVectorRenderer( def: QueryBuilderOperationDef, innerExpr: string ) { - let rangeVector = (model.params ?? [])[0] ?? 'auto'; - - if (rangeVector === 'auto') { - rangeVector = '$__interval'; - } - + let rangeVector = (model.params ?? [])[0] ?? '$__interval'; return `${def.id}(${innerExpr} [${rangeVector}])`; } diff --git a/public/app/plugins/datasource/prometheus/querybuilder/PromQueryModeller.test.ts b/public/app/plugins/datasource/prometheus/querybuilder/PromQueryModeller.test.ts index 1c2e18f8600..cdf8c5e4a50 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/PromQueryModeller.test.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/PromQueryModeller.test.ts @@ -105,7 +105,7 @@ describe('PromQueryModeller', () => { modeller.renderQuery({ metric: 'metric', labels: [{ label: 'pod', op: '=', value: 'A' }], - operations: [{ id: PromOperationId.Rate, params: ['auto'] }], + operations: [{ id: PromOperationId.Rate, params: ['$__rate_interval'] }], }) ).toBe('rate(metric{pod="A"}[$__rate_interval])'); }); @@ -115,9 +115,9 @@ describe('PromQueryModeller', () => { modeller.renderQuery({ metric: 'metric', labels: [{ label: 'pod', op: '=', value: 'A' }], - operations: [{ id: PromOperationId.Increase, params: ['auto'] }], + operations: [{ id: PromOperationId.Increase, params: ['$__interval'] }], }) - ).toBe('increase(metric{pod="A"}[$__rate_interval])'); + ).toBe('increase(metric{pod="A"}[$__interval])'); }); it('Can render rate with custom range-vector', () => { @@ -283,18 +283,18 @@ describe('PromQueryModeller', () => { modeller.renderQuery({ metric: 'metric_a', labels: [], - operations: [{ id: 'holt_winters', params: ['auto', 0.5, 0.5] }], + operations: [{ id: 'holt_winters', params: ['5m', 0.5, 0.5] }], }) - ).toBe('holt_winters(metric_a[$__rate_interval], 0.5, 0.5)'); + ).toBe('holt_winters(metric_a[5m], 0.5, 0.5)'); }); it('Can render functions that require parameters left of a range', () => { expect( modeller.renderQuery({ metric: 'metric_a', labels: [], - operations: [{ id: 'quantile_over_time', params: ['auto', 1] }], + operations: [{ id: 'quantile_over_time', params: ['5m', 1] }], }) - ).toBe('quantile_over_time(1, metric_a[$__rate_interval])'); + ).toBe('quantile_over_time(1, metric_a[5m])'); }); it('Can render the label_join function', () => { expect( diff --git a/public/app/plugins/datasource/prometheus/querybuilder/aggregations.ts b/public/app/plugins/datasource/prometheus/querybuilder/aggregations.ts index 4f0efddeeb9..6fc94bbc85e 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/aggregations.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/aggregations.ts @@ -5,6 +5,7 @@ import { defaultAddOperationHandler, functionRendererLeft, getPromAndLokiOperationDisplayName, + getRangeVectorParamDef, } from './shared/operationUtils'; import { QueryBuilderOperation, QueryBuilderOperationDef, QueryBuilderOperationParamDef } from './shared/types'; import { PromVisualQueryOperationCategory, PromOperationId } from './types'; @@ -25,6 +26,7 @@ export function getAggregationOperations(): QueryBuilderOperationDef[] { createAggregationOverTime(PromOperationId.CountOverTime), createAggregationOverTime(PromOperationId.LastOverTime), createAggregationOverTime(PromOperationId.PresentOverTime), + createAggregationOverTime(PromOperationId.AbsentOverTime), createAggregationOverTime(PromOperationId.StddevOverTime), ]; } @@ -175,8 +177,8 @@ function createAggregationOverTime(name: string): QueryBuilderOperationDef { return { id: name, name: getPromAndLokiOperationDisplayName(name), - params: [getAggregationOverTimeRangeVector()], - defaultParams: ['auto'], + params: [getRangeVectorParamDef()], + defaultParams: ['$__interval'], alternativesKey: 'overtime function', category: PromVisualQueryOperationCategory.RangeFunctions, renderer: operationWithRangeVectorRenderer, @@ -184,24 +186,11 @@ function createAggregationOverTime(name: string): QueryBuilderOperationDef { }; } -function getAggregationOverTimeRangeVector(): QueryBuilderOperationParamDef { - return { - name: 'Range vector', - type: 'string', - options: ['auto', '$__interval', '$__range', '1m', '5m', '10m', '1h', '24h'], - }; -} - function operationWithRangeVectorRenderer( model: QueryBuilderOperation, def: QueryBuilderOperationDef, innerExpr: string ) { - let rangeVector = (model.params ?? [])[0] ?? 'auto'; - - if (rangeVector === 'auto') { - rangeVector = '$__interval'; - } - + let rangeVector = (model.params ?? [])[0] ?? '$__interval'; return `${def.id}(${innerExpr}[${rangeVector}])`; } diff --git a/public/app/plugins/datasource/prometheus/querybuilder/operations.ts b/public/app/plugins/datasource/prometheus/querybuilder/operations.ts index d1f6ecf22cf..59a037e4f22 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/operations.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/operations.ts @@ -4,13 +4,13 @@ import { functionRendererLeft, functionRendererRight, getPromAndLokiOperationDisplayName, + getRangeVectorParamDef, rangeRendererLeftWithParams, rangeRendererRightWithParams, } from './shared/operationUtils'; import { QueryBuilderOperation, QueryBuilderOperationDef, - QueryBuilderOperationParamDef, QueryWithOperations, VisualQueryModeller, } from './shared/types'; @@ -51,10 +51,45 @@ export function getOperationDefinitions(): QueryBuilderOperationDef[] { addOperationHandler: defaultAddOperationHandler, }, createRangeFunction(PromOperationId.Changes), - createRangeFunction(PromOperationId.Rate), + createRangeFunction(PromOperationId.Rate, true), createRangeFunction(PromOperationId.Irate), - createRangeFunction(PromOperationId.Increase), + 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, + }), // 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 { @@ -85,7 +120,6 @@ export function getOperationDefinitions(): QueryBuilderOperationDef[] { addOperationHandler: addNestedQueryHandler, }, createFunction({ id: PromOperationId.Absent }), - createRangeFunction(PromOperationId.AbsentOverTime), createFunction({ id: PromOperationId.Acos, category: PromVisualQueryOperationCategory.Trigonometric, @@ -163,20 +197,7 @@ export function getOperationDefinitions(): QueryBuilderOperationDef[] { createFunction({ id: PromOperationId.Exp }), createFunction({ id: PromOperationId.Floor }), createFunction({ id: PromOperationId.Group }), - createFunction({ - id: PromOperationId.HoltWinters, - params: [ - getRangeVectorParamDef(), - { name: 'Smoothing Factor', type: 'number' }, - { name: 'Trend Factor', type: 'number' }, - ], - defaultParams: ['auto', 0.5, 0.5], - alternativesKey: 'range function', - category: PromVisualQueryOperationCategory.RangeFunctions, - renderer: rangeRendererRightWithParams, - }), createFunction({ id: PromOperationId.Hour }), - createRangeFunction(PromOperationId.Idelta), createFunction({ id: PromOperationId.LabelJoin, params: [ @@ -209,28 +230,12 @@ export function getOperationDefinitions(): QueryBuilderOperationDef[] { id: PromOperationId.Pi, renderer: (model) => `${model.id}()`, }), - createFunction({ - id: PromOperationId.PredictLinear, - params: [getRangeVectorParamDef(), { name: 'Seconds from now', type: 'number' }], - defaultParams: ['auto', 60], - alternativesKey: 'range function', - category: PromVisualQueryOperationCategory.RangeFunctions, - renderer: rangeRendererRightWithParams, - }), createFunction({ id: PromOperationId.Quantile, params: [{ name: 'Value', type: 'number' }], defaultParams: [1], renderer: functionRendererLeft, }), - createFunction({ - id: PromOperationId.QuantileOverTime, - params: [getRangeVectorParamDef(), { name: 'Quantile', type: 'number' }], - defaultParams: ['auto', 0.5], - alternativesKey: 'range function', - category: PromVisualQueryOperationCategory.RangeFunctions, - renderer: rangeRendererLeftWithParams, - }), createFunction({ id: PromOperationId.Rad }), createRangeFunction(PromOperationId.Resets), createFunction({ @@ -288,30 +293,40 @@ export function createFunction(definition: Partial): Q }; } -export function createRangeFunction(name: string): QueryBuilderOperationDef { +export function createRangeFunction(name: string, withRateInterval = false): QueryBuilderOperationDef { return { id: name, name: getPromAndLokiOperationDisplayName(name), - params: [getRangeVectorParamDef()], - defaultParams: ['auto'], + 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] ?? 'auto'; - - if (rangeVector === 'auto') { - rangeVector = '$__rate_interval'; - } - + let rangeVector = (model.params ?? [])[0] ?? '5m'; return `${def.id}(${innerExpr}[${rangeVector}])`; } @@ -321,14 +336,6 @@ function getSimpleBinaryRenderer(operator: string) { }; } -function getRangeVectorParamDef(): QueryBuilderOperationParamDef { - return { - name: 'Range vector', - type: 'string', - options: ['auto', '$__rate_interval', '$__interval', '$__range', '1m', '5m', '10m', '1h', '24h'], - }; -} - /** * Since there can only be one operation with range vector this will replace the current one (if one was added ) */ @@ -337,28 +344,22 @@ export function addOperationWithRangeVector( query: PromVisualQuery, modeller: VisualQueryModeller ) { + const newOperation: QueryBuilderOperation = { + id: def.id, + params: def.defaultParams, + }; + if (query.operations.length > 0) { const firstOp = modeller.getOperationDef(query.operations[0].id); if (firstOp.addOperationHandler === addOperationWithRangeVector) { return { ...query, - operations: [ - { - ...query.operations[0], - id: def.id, - }, - ...query.operations.slice(1), - ], + operations: [newOperation, ...query.operations.slice(1)], }; } } - const newOperation: QueryBuilderOperation = { - id: def.id, - params: def.defaultParams, - }; - return { ...query, operations: [newOperation, ...query.operations], diff --git a/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationName.tsx b/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationName.tsx index c8f234575ab..1abef104fb6 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationName.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationName.tsx @@ -60,10 +60,9 @@ export const OperationName = React.memo(({ operation, def, index, onChang onCloseMenu={onToggleSwitcher} onChange={(value) => { if (value.value) { - onChange(index, { - ...operation, - id: value.value.id, - }); + const newDef = queryModeller.getOperationDef(value.value.id); + let changedOp = { ...operation, id: value.value.id }; + onChange(index, def.changeTypeHandler ? def.changeTypeHandler(changedOp, newDef) : changedOp); } }} /> diff --git a/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationParamEditor.tsx b/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationParamEditor.tsx index 168d880342c..3549b82a2ac 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationParamEditor.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationParamEditor.tsx @@ -1,4 +1,4 @@ -import { toOption } from '@grafana/data'; +import { SelectableValue, toOption } from '@grafana/data'; import { Input, Select } from '@grafana/ui'; import React, { ComponentType } from 'react'; import { QueryBuilderOperationParamDef, QueryBuilderOperationParamEditorProps } from '../shared/types'; @@ -45,16 +45,22 @@ function SelectInputParamEditor({ operationIndex, onChange, }: QueryBuilderOperationParamEditorProps) { - const selectOptions = paramDef.options!.map((option) => ({ - label: option as string, - value: option as string, - })); + let selectOptions = paramDef.options as Array>; + + if (!selectOptions[0]?.label) { + selectOptions = paramDef.options!.map((option) => ({ + label: option.toString(), + value: option as string, + })); + } + + let valueOption = selectOptions.find((x) => x.value === value) ?? toOption(value as string); return (