From 1fc57d8fd549c6057a860c0d246a2bb96979443d Mon Sep 17 00:00:00 2001 From: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com> Date: Wed, 17 Jul 2024 09:25:46 -0700 Subject: [PATCH] Transformations: Fix null for reduceSeriesToRows (#90508) * Transformations: Fix null for reduceSeriesToRows * Add tests with null data frames --- .../transformers/reduce.test.ts | 109 ++++++++++++++++++ .../transformations/transformers/reduce.ts | 7 +- 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/packages/grafana-data/src/transformations/transformers/reduce.test.ts b/packages/grafana-data/src/transformations/transformers/reduce.test.ts index 5d66b7ec67f..bae31b5b8f9 100644 --- a/packages/grafana-data/src/transformations/transformers/reduce.test.ts +++ b/packages/grafana-data/src/transformations/transformers/reduce.test.ts @@ -27,6 +27,14 @@ const seriesAWithMultipleFields = toDataFrame({ ], }); +const seriesAWithAllNulls = toDataFrame({ + name: 'A', + fields: [ + { name: 'time', type: FieldType.time, values: [3000, 4000, 5000, 6000] }, + { name: 'temperature', type: FieldType.number, values: [null, null, null, null, null] }, + ], +}); + const seriesBWithSingleField = toDataFrame({ name: 'B', fields: [ @@ -44,6 +52,14 @@ const seriesBWithMultipleFields = toDataFrame({ ], }); +const seriesBWithAllNulls = toDataFrame({ + name: 'B', + fields: [ + { name: 'time', type: FieldType.time, values: [3000, 4000, 5000, 6000] }, + { name: 'temperature', type: FieldType.number, values: [null, null, null, null, null] }, + ], +}); + describe('Reducer Transformer', () => { beforeAll(() => { mockTransformationsRegistry([reduceTransformer]); @@ -177,6 +193,99 @@ describe('Reducer Transformer', () => { ); }); + it('reduces multiple data frames, one having all null values', async () => { + const cfg = { + id: DataTransformerID.reduce, + options: { + reducers: [ReducerID.first, ReducerID.min, ReducerID.max, ReducerID.last], + }, + }; + + await expect(transformDataFrame([cfg], [seriesAWithAllNulls, seriesBWithSingleField])).toEmitValuesWith( + (received) => { + const processed = received[0]; + const expected: Field[] = [ + { + name: 'Field', + type: FieldType.string, + values: ['A temperature', 'B temperature'], + config: {}, + }, + { + name: 'First', + type: FieldType.number, + values: [NaN, 1], + config: {}, + }, + { + name: 'Min', + type: FieldType.number, + values: [NaN, 1], + config: {}, + }, + { + name: 'Max', + type: FieldType.number, + values: [NaN, 7], + config: {}, + }, + { + name: 'Last', + type: FieldType.number, + values: [NaN, 7], + config: {}, + }, + ]; + + expect(processed.length).toEqual(1); + expect(processed[0].length).toEqual(2); + expect(processed[0].fields).toEqual(expected); + } + ); + + await expect(transformDataFrame([cfg], [seriesAWithSingleField, seriesBWithAllNulls])).toEmitValuesWith( + (received) => { + const processed = received[0]; + const expected: Field[] = [ + { + name: 'Field', + type: FieldType.string, + values: ['A temperature', 'B temperature'], + config: {}, + }, + { + name: 'First', + type: FieldType.number, + values: [3, NaN], + config: {}, + }, + { + name: 'Min', + type: FieldType.number, + values: [3, NaN], + config: {}, + }, + { + name: 'Max', + type: FieldType.number, + values: [6, NaN], + config: {}, + }, + { + name: 'Last', + type: FieldType.number, + values: [6, NaN], + config: {}, + }, + ]; + + expect(processed.length).toEqual(1); + expect(processed[0].length).toEqual(2); + expect(processed[0].fields).toEqual(expected); + } + ); + }); + it('reduces single data frame with many fields', async () => { const cfg = { id: DataTransformerID.reduce, diff --git a/packages/grafana-data/src/transformations/transformers/reduce.ts b/packages/grafana-data/src/transformations/transformers/reduce.ts index 22b19705a65..ad17991d3bd 100644 --- a/packages/grafana-data/src/transformations/transformers/reduce.ts +++ b/packages/grafana-data/src/transformations/transformers/reduce.ts @@ -132,7 +132,12 @@ function reduceSeriesToRows( for (const info of calculators) { const v = results[info.id]; - calcs[info.id][i] = v; + if (v === null) { + // NaN ensures proper row index, null results in shift + calcs[info.id][i] = NaN; + } else { + calcs[info.id][i] = v; + } } }