Transformations: Preserve base threshold when using Config from query results (#96781)

* Merge in default threshold base if exists

* Fix tests to not expect default threshold

* Add config from query example to gdev

* Add a test around the merging of thresholds

* Revert "Add config from query example to gdev"

This reverts commit a239224ebb.

* update gdev

* Add note about keeping the base threshold

* Update public/app/features/transformers/docs/content.ts

Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>

* Merge in generated code

---------

Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
This commit is contained in:
Kristina 2024-11-21 12:46:29 -06:00 committed by GitHub
parent e9fae5bd7f
commit f9eb013334
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 869 additions and 552 deletions

View File

@ -3,7 +3,10 @@
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
@ -20,19 +23,28 @@
},
"description": "",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
@ -41,6 +53,7 @@
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
@ -49,6 +62,10 @@
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "line"
}
@ -58,7 +75,8 @@
"mode": "absolute",
"steps": [
{
"color": "green"
"color": "green",
"value": null
},
{
"color": "red",
@ -80,7 +98,8 @@
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
@ -89,6 +108,10 @@
},
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"hide": false,
"max": 100,
"min": 1,
@ -99,6 +122,10 @@
{
"alias": "",
"csvContent": "min,max,threshold1\n1000,1000,8000\n0,100,80\n\n",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"refId": "config",
"scenarioId": "csv_content"
}
@ -116,6 +143,10 @@
"type": "timeseries"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
@ -123,14 +154,18 @@
},
"custom": {
"align": "left",
"displayMode": "auto"
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
"color": "green",
"value": null
},
{
"color": "red",
@ -147,8 +182,10 @@
},
"properties": [
{
"id": "custom.displayMode",
"value": "color-text"
"id": "custom.cellOptions",
"value": {
"type": "color-text"
}
}
]
}
@ -162,12 +199,25 @@
},
"id": 5,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "8.1.0-pre",
"pluginVersion": "11.4.0-pre",
"targets": [
{
"csvContent": "Name, Value, SensorA, MyUnit, MyColor\nGoogle, 10, 50, km/h, blue\nGoogle, 100, 100,km/h, orange\n",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"hide": false,
"refId": "A",
"scenarioId": "csv_content"
@ -201,6 +251,10 @@
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
@ -208,14 +262,18 @@
},
"custom": {
"align": "center",
"displayMode": "auto"
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
"color": "green",
"value": null
},
{
"color": "red",
@ -232,8 +290,11 @@
},
"properties": [
{
"id": "custom.displayMode",
"value": "color-background-solid"
"id": "custom.cellOptions",
"value": {
"mode": "basic",
"type": "color-background"
}
}
]
}
@ -247,12 +308,25 @@
},
"id": 7,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "8.1.0-pre",
"pluginVersion": "11.4.0-pre",
"targets": [
{
"csvContent": "ID, DisplayText\n21412312312, Homer\n12421412413, Simpsons \n12321312313, Bart",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"hide": false,
"refId": "A",
"scenarioId": "csv_content"
@ -281,6 +355,10 @@
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
@ -288,14 +366,18 @@
},
"custom": {
"align": "center",
"displayMode": "auto"
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
"color": "green",
"value": null
},
{
"color": "red",
@ -312,8 +394,11 @@
},
"properties": [
{
"id": "custom.displayMode",
"value": "color-background-solid"
"id": "custom.cellOptions",
"value": {
"mode": "basic",
"type": "color-background"
}
}
]
}
@ -327,12 +412,25 @@
},
"id": 6,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "8.1.0-pre",
"pluginVersion": "11.4.0-pre",
"targets": [
{
"csvContent": "Value, Color\nOK, blue\nPretty bad, red\nYay it's green, green\nSomething is off, orange\nNo idea, #88AA00\nAm I purple?, purple",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"hide": false,
"refId": "A",
"scenarioId": "csv_content"
@ -365,6 +463,10 @@
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
@ -372,14 +474,18 @@
},
"custom": {
"align": "center",
"displayMode": "auto"
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
"color": "green",
"value": null
},
{
"color": "red",
@ -398,12 +504,25 @@
},
"id": 8,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "8.1.0-pre",
"pluginVersion": "11.4.0-pre",
"targets": [
{
"csvContent": "ID, Value\n21412312312, 100\n12421412413, 20\n12321312313, 10",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"hide": false,
"refId": "A",
"scenarioId": "csv_content"
@ -432,12 +551,19 @@
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"axisSoftMin": 0,
@ -448,14 +574,21 @@
"tooltip": false,
"viz": false
},
"lineWidth": 1
"lineWidth": 1,
"scaleDistribution": {
"type": "linear"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
"color": "green",
"value": null
},
{
"color": "red",
@ -474,31 +607,45 @@
},
"id": 9,
"options": {
"barRadius": 0,
"barWidth": 0.97,
"fullHighlight": false,
"groupWidth": 0.7,
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
"placement": "bottom",
"showLegend": true
},
"orientation": "horizontal",
"showValue": "auto",
"stacking": "none",
"text": {},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"xTickLabelRotation": 0,
"xTickLabelSpacing": 0
},
"pluginVersion": "8.1.0-pre",
"targets": [
{
"csvContent": "ID, Value\nA21412312312, 100\nA12421412413, 20\nA12321312313, 10\n",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"hide": false,
"refId": "data",
"scenarioId": "csv_content"
},
{
"csvContent": "ID, DisplayText\nA21412312312, Homer\nA12421412413, Marge \nA12321312313, Bart",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"hide": false,
"refId": "mappings",
"scenarioId": "csv_content"
@ -529,10 +676,120 @@
}
],
"type": "barchart"
},
{
"datasource": {
"default": true,
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "dashed+area"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 10,
"w": 12,
"x": 12,
"y": 19
},
"id": 10,
"maxDataPoints": 100,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"meta\": {\n \"typeVersion\": [\n 0,\n 0\n ],\n \"custom\": {\n \"customStat\": 10\n }\n },\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {\n \"interval\": 300000\n }\n },\n {\n \"name\": \"A-series\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"labels\": {},\n \"config\": {}\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1732120345536,\n 1732120645536,\n 1732120945536,\n 1732121245536,\n 1732121545536,\n 1732121845536,\n 1732122145536,\n 1732122445536,\n 1732122745536,\n 1732123045536,\n 1732123345536,\n 1732123645536,\n 1732123945536,\n 1732124245536,\n 1732124545536,\n 1732124845536,\n 1732125145536,\n 1732125445536,\n 1732125745536,\n 1732126045536,\n 1732126345536,\n 1732126645536,\n 1732126945536,\n 1732127245536,\n 1732127545536,\n 1732127845536,\n 1732128145536,\n 1732128445536,\n 1732128745536,\n 1732129045536,\n 1732129345536,\n 1732129645536,\n 1732129945536,\n 1732130245536,\n 1732130545536,\n 1732130845536,\n 1732131145536,\n 1732131445536,\n 1732131745536,\n 1732132045536,\n 1732132345536,\n 1732132645536,\n 1732132945536,\n 1732133245536,\n 1732133545536,\n 1732133845536,\n 1732134145536,\n 1732134445536,\n 1732134745536,\n 1732135045536,\n 1732135345536,\n 1732135645536,\n 1732135945536,\n 1732136245536,\n 1732136545536,\n 1732136845536,\n 1732137145536,\n 1732137445536,\n 1732137745536,\n 1732138045536,\n 1732138345536,\n 1732138645536,\n 1732138945536,\n 1732139245536,\n 1732139545536,\n 1732139845536,\n 1732140145536,\n 1732140445536,\n 1732140745536,\n 1732141045536,\n 1732141345536,\n 1732141645536\n ],\n [\n 36.67835770082578,\n 35.674537924065,\n 8.339763723800829,\n 16.313291374141446,\n 66.05915891584247,\n 55.975417240601566,\n 33.75563648171818,\n 10.561077849025175,\n 20.31936089572975,\n 26.11219409670694,\n 57.542750561307564,\n 67.10954340535248,\n 82.95323961635275,\n 100.9691805551439,\n 59.829706792214644,\n 94.58723331927925,\n 89.3082374466047,\n 58.69065135820439,\n 97.144192150251,\n 139.99199318295675,\n 157.9473973408396,\n 177.94452058033198,\n 188.84065573954362,\n 154.3930906887033,\n 130.14406878049226,\n 116.65818233680316,\n 100.96041794526472,\n 144.65142921584447,\n 175.75178611497054,\n 203.55271609883386,\n 238.4931714915047,\n 253.38754249911452,\n 271.1735238723396,\n 258.54418620287515,\n 260.8463123020904,\n 216.10614084307323,\n 253.30389406688175,\n 249.37108721413884,\n 243.7226799137106,\n 216.74579233434042,\n 262.50043010512826,\n 238.71564300219498,\n 218.3552317737898,\n 195.6154411937393,\n 154.1987522722987,\n 124.00066408416897,\n 146.6474694384778,\n 101.68405646311294,\n 104.5791139459948,\n 85.39428966503652,\n 78.45166775446714,\n 56.285707917841535,\n 36.22861441808941,\n 35.098428846082555,\n 68.67835646605371,\n 101.67142528391042,\n 151.04038339587296,\n 114.77414457402928,\n 72.65341528313934,\n 113.42643748928826,\n 151.09282092262364,\n 163.24422498859587,\n 183.86606816236363,\n 230.24678524811478,\n 205.94887669562561,\n 211.24387656976373,\n 217.26738326873522,\n 214.66898480692646,\n 206.95531499977153,\n 194.19724584765092,\n 146.16071387746757,\n 188.30193538777615\n ]\n ]\n }\n }\n]",
"refId": "A",
"scenarioId": "raw_frame"
}
],
"title": "Config from query / threshold does not overwrite Base threshold",
"transformations": [
{
"id": "configFromData",
"options": {
"configRefId": "A",
"mappings": [
{
"fieldName": "A-series",
"handlerArguments": {
"threshold": {
"color": "yellow"
}
},
"handlerKey": "threshold1"
}
]
}
}
],
"type": "timeseries"
}
],
"refresh": "",
"schemaVersion": 33,
"schemaVersion": 40,
"tags": [
"gdev",
"transform"
@ -541,12 +798,13 @@
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
"from": "2024-11-20T16:31:51.747Z",
"to": "2024-11-20T22:28:15.688Z"
},
"timepicker": {},
"timezone": "",
"title": "Transforms - Config from query",
"uid": "Juj4_7ink",
"version": 1
}
"version": 1,
"weekStart": ""
}

View File

@ -286,6 +286,8 @@ In the field mapping specify:
Grafana builds value mappings from your query result and applies them to the real data query results. You should see values being mapped and colored according to the config query results.
> **Note:** When you use this transformation for thresholds, the visualization continues to use the panel's base threshold.
### Convert field type
Use this transformation to modify the field type of a specified field.

View File

@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`setFieldConfigDefaults applies the base threshold when one does not exist in the config 1`] = `
{
"mode": "absolute",
"steps": [
{
"color": "red",
"value": -Infinity,
},
{
"color": "green",
"value": 50,
},
],
}
`;

View File

@ -730,6 +730,33 @@ describe('setFieldConfigDefaults', () => {
}
`);
});
it('applies the base threshold when one does not exist in the config', () => {
const defaultConfig: FieldConfig = {
thresholds: {
mode: ThresholdsMode.Absolute,
steps: [{ value: -Infinity, color: 'red' }],
},
};
const config: FieldConfig = {
thresholds: {
mode: ThresholdsMode.Absolute,
steps: [{ value: 50, color: 'green' }],
},
};
const context: FieldOverrideEnv = {
data: [],
field: { type: FieldType.number } as Field,
dataFrameIndex: 0,
fieldConfigRegistry: customFieldRegistry,
};
setFieldConfigDefaults(config, defaultConfig, context);
expect(config.thresholds).toMatchSnapshot();
});
});
describe('setDynamicConfigValue', () => {

View File

@ -2,7 +2,7 @@ import { isNumber, set, unset, get, cloneDeep } from 'lodash';
import { useMemo, useRef } from 'react';
import usePrevious from 'react-use/lib/usePrevious';
import { VariableFormatID } from '@grafana/schema';
import { ThresholdsMode, VariableFormatID } from '@grafana/schema';
import { compareArrayValues, compareDataFrameStructures } from '../dataframe/frameComparisons';
import { guessFieldTypeForField } from '../dataframe/processDataFrame';
@ -347,6 +347,18 @@ export function setFieldConfigDefaults(config: FieldConfig, defaults: FieldConfi
// Combine the data source links and the panel default config links
config.links = [...config.links, ...defaults.links];
}
// if we have a base threshold set by default but not on the config, we need to merge it in
const defaultBaseStep =
defaults?.thresholds?.mode === ThresholdsMode.Absolute &&
defaults.thresholds?.steps.find((step) => step.value === -Infinity);
if (
config.thresholds?.mode === ThresholdsMode.Absolute &&
!config.thresholds.steps.some((step) => step.value === -Infinity) &&
defaultBaseStep
) {
config.thresholds.steps = [defaultBaseStep, ...config.thresholds.steps];
}
for (const fieldConfigProperty of context.fieldConfigRegistry.list()) {
if (fieldConfigProperty.isCustom && !config.custom) {
config.custom = {};

View File

@ -93,7 +93,7 @@ describe('config from data', () => {
const results = extractConfigFromQuery(options, [config, seriesA]);
expect(results.length).toBe(1);
const thresholdConfig = results[0].fields[1].config.thresholds?.steps[1];
const thresholdConfig = results[0].fields[1].config.thresholds?.steps[0];
expect(thresholdConfig).toBeDefined();
expect(thresholdConfig?.color).toBe('orange');
expect(thresholdConfig?.value).toBe(50);

View File

@ -179,6 +179,9 @@ In the field mapping specify:
| Color | Value mappings / Color | All values |
Grafana builds value mappings from your query result and applies them to the real data query results. You should see values being mapped and colored according to the config query results.
> **Note:** When you use this transformation for thresholds, the visualization continues to use the panel's base threshold.
`;
},
},
@ -1251,7 +1254,6 @@ Select this option to transform the time series data frame from the long format
| 2023-01-01 00:00:00 | 10 | 20 |
| 2023-01-01 01:00:00 | 15 | 25 |
> **Note:** This transformation is available in Grafana 7.5.10+ and Grafana 8.0.6+.
`;
},
links: [
@ -1453,7 +1455,6 @@ Here is the result after applying the Series to rows transformation.
This transformation facilitates the consolidation of results from multiple time series queries, providing a streamlined and unified dataset for efficient analysis and visualization in a tabular format.
> **Note:** This transformation is available in Grafana 7.1+.
`;
},
},

View File

@ -169,7 +169,7 @@ export const configMapHandlers: FieldToConfigMapHandler[] = [
if (!config.thresholds) {
config.thresholds = {
mode: ThresholdsMode.Absolute,
steps: [{ value: -Infinity, color: 'green' }],
steps: [],
};
}

View File

@ -111,7 +111,7 @@ describe('Rows to fields', () => {
});
const result = rowsToFields({}, input);
expect(result.fields[0].config.thresholds?.steps[1].value).toBe(30);
expect(result.fields[0].config.thresholds?.steps[0].value).toBe(30);
});
it('Will extract other string fields to labels', () => {