From 83917e61fd5483fcaf72ae1adcaad63b1de28afd Mon Sep 17 00:00:00 2001 From: "Grot (@grafanabot)" <43478413+grafanabot@users.noreply.github.com> Date: Thu, 16 Jun 2022 18:46:13 -0400 Subject: [PATCH] StateTimeline: make sure we use result of applyNullInsertThreshold() (#50988) (#50994) Co-authored-by: Kyle Cunningham (cherry picked from commit 406509cdd3af702879c857390850fd3e3edc47ed) Co-authored-by: Leon Sorokin --- .../GraphNG/nullInsertThreshold.test.ts | 33 ++++++++++--- .../components/GraphNG/nullInsertThreshold.ts | 48 ++++++++++++------- .../app/plugins/panel/state-timeline/utils.ts | 8 ++-- 3 files changed, 59 insertions(+), 30 deletions(-) diff --git a/packages/grafana-ui/src/components/GraphNG/nullInsertThreshold.test.ts b/packages/grafana-ui/src/components/GraphNG/nullInsertThreshold.test.ts index 6373e8884b8..ea2fe6f50f6 100644 --- a/packages/grafana-ui/src/components/GraphNG/nullInsertThreshold.test.ts +++ b/packages/grafana-ui/src/components/GraphNG/nullInsertThreshold.test.ts @@ -111,12 +111,14 @@ describe('nullInsertThreshold Transformer', () => { const result = applyNullInsertThreshold({ frame: df, refFieldName: null, - refFieldPseudoMin: 1, + refFieldPseudoMin: -0.5, refFieldPseudoMax: 13, }); - expect(result.fields[0].values.toArray()).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); + expect(result.fields[0].values.toArray()).toStrictEqual([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); expect(result.fields[1].values.toArray()).toStrictEqual([ + null, + null, null, null, null, @@ -132,6 +134,8 @@ describe('nullInsertThreshold Transformer', () => { 8, ]); expect(result.fields[2].values.toArray()).toStrictEqual([ + null, + null, null, null, null, @@ -160,7 +164,7 @@ describe('nullInsertThreshold Transformer', () => { const result = applyNullInsertThreshold({ frame: df, refFieldName: null, refFieldPseudoMax: 13 }); - expect(result.fields[0].values.toArray()).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); + expect(result.fields[0].values.toArray()).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); expect(result.fields[1].values.toArray()).toStrictEqual([ 4, null, @@ -174,7 +178,6 @@ describe('nullInsertThreshold Transformer', () => { 8, null, null, - null, ]); expect(result.fields[2].values.toArray()).toStrictEqual([ 'a', @@ -189,7 +192,6 @@ describe('nullInsertThreshold Transformer', () => { 'c', null, null, - null, ]); // should work for frames with 1 datapoint @@ -202,15 +204,32 @@ describe('nullInsertThreshold Transformer', () => { ], }); - // Max is 2 as opposed to the above 13 otherwise + // Max is 2.5 as opposed to the above 13 otherwise // we get 12 nulls instead of the additional 1 - const result2 = applyNullInsertThreshold({ frame: df2, refFieldName: null, refFieldPseudoMax: 2 }); + const result2 = applyNullInsertThreshold({ frame: df2, refFieldName: null, refFieldPseudoMax: 2.5 }); expect(result2.fields[0].values.toArray()).toStrictEqual([1, 2]); expect(result2.fields[1].values.toArray()).toStrictEqual([1, null]); expect(result2.fields[2].values.toArray()).toStrictEqual(['a', null]); }); + test('should not insert trailing null at end +interval when timeRange.to.valueOf() equals threshold', () => { + const df = new MutableDataFrame({ + refId: 'A', + fields: [ + { name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [1] }, + { name: 'One', type: FieldType.number, values: [1] }, + { name: 'Two', type: FieldType.string, values: ['a'] }, + ], + }); + + const result = applyNullInsertThreshold({ frame: df, refFieldName: null, refFieldPseudoMax: 2 }); + + expect(result.fields[0].values.toArray()).toStrictEqual([1]); + expect(result.fields[1].values.toArray()).toStrictEqual([1]); + expect(result.fields[2].values.toArray()).toStrictEqual(['a']); + }); + // TODO: make this work test.skip('should insert nulls at +threshold (when defined) instead of +interval', () => { const df = new MutableDataFrame({ diff --git a/packages/grafana-ui/src/components/GraphNG/nullInsertThreshold.ts b/packages/grafana-ui/src/components/GraphNG/nullInsertThreshold.ts index cd996e1179c..3b6cdb0e6c8 100644 --- a/packages/grafana-ui/src/components/GraphNG/nullInsertThreshold.ts +++ b/packages/grafana-ui/src/components/GraphNG/nullInsertThreshold.ts @@ -1,4 +1,4 @@ -import { ArrayVector, DataFrame, FieldType } from '@grafana/data'; +import { ArrayVector, DataFrame, FieldType, incrRoundDn } from '@grafana/data'; type InsertMode = (prev: number, next: number, threshold: number) => number; @@ -76,10 +76,21 @@ export function applyNullInsertThreshold(opts: NullInsertOptions): DataFrame { return { ...frame, length: filledFieldValues[0].length, - fields: frame.fields.map((field, i) => ({ - ...field, - values: new ArrayVector(filledFieldValues[i]), - })), + fields: frame.fields.map((field, i) => { + let f = { + ...field, + values: new ArrayVector(filledFieldValues[i]), + }; + + if (i === 0) { + f.state = { + ...field.state, + nullThresholdApplied: true, + }; + } + + return f; + }), }; } @@ -101,24 +112,25 @@ function nullInsertThreshold( thorough: boolean ) { const len = refValues.length; - let prevValue: number = refValues[0]; const refValuesNew: number[] = []; - // Continiuously add the threshold to the minimum value - // While this is less than "prevValue" which is the lowest - // time value in the sequence add in time frames - if (refFieldPseudoMin != null) { - let minValue = refFieldPseudoMin - threshold; + // Continiuously subtract the threshold from the first data + // point filling in insert values accordingly + if (refFieldPseudoMin != null && refFieldPseudoMin < refValues[0]) { + // this will be 0 or 1 threshold increment left of visible range + let prevSlot = incrRoundDn(refFieldPseudoMin, threshold); - while (minValue < prevValue - threshold) { - let nextValue = minValue + threshold; - refValuesNew.push(getInsertValue(minValue, nextValue, threshold)); - minValue = nextValue; + while (prevSlot < refValues[0]) { + // (prevSlot - threshold) is used to simulate the previous 'real' data point, as getInsertValue expects + refValuesNew.push(getInsertValue(prevSlot - threshold, prevSlot, threshold)); + prevSlot += threshold; } } // Insert initial value - refValuesNew.push(prevValue); + refValuesNew.push(refValues[0]); + + let prevValue: number = refValues[0]; // Fill nulls when a value is greater than // the threshold value @@ -141,8 +153,8 @@ function nullInsertThreshold( } // At the end of the sequence - if (refFieldPseudoMax != null) { - while (prevValue + threshold <= refFieldPseudoMax) { + if (refFieldPseudoMax != null && refFieldPseudoMax > prevValue) { + while (prevValue + threshold < refFieldPseudoMax) { refValuesNew.push(getInsertValue(prevValue, refFieldPseudoMax, threshold)); prevValue += threshold; } diff --git a/public/app/plugins/panel/state-timeline/utils.ts b/public/app/plugins/panel/state-timeline/utils.ts index d85ca7dd67e..c6e7756a061 100644 --- a/public/app/plugins/panel/state-timeline/utils.ts +++ b/public/app/plugins/panel/state-timeline/utils.ts @@ -401,11 +401,9 @@ export function prepareTimelineFields( refFieldPseudoMax: timeRange.to.valueOf(), }); - // Mark the field state as having a null threhold applied - frame.fields[0].state = { - ...frame.fields[0].state, - nullThresholdApplied: true, - }; + if (nulledFrame !== frame) { + changed = true; + } const fields: Field[] = []; for (let field of nullToValue(nulledFrame).fields) {