mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DataFrames: add utility function to check if structure has changed (#29006)
* add common flag for knowing if the structure changes * remove property * fix test * fix test * update comment * fix jsdoc comments
This commit is contained in:
parent
01df8f177c
commit
88b59ae3c7
95
packages/grafana-data/src/dataframe/frameComparisons.test.ts
Normal file
95
packages/grafana-data/src/dataframe/frameComparisons.test.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { FieldType } from '../types/dataFrame';
|
||||||
|
import { compareDataFrameStructures, compareArrayValues } from './frameComparisons';
|
||||||
|
import { toDataFrame } from './processDataFrame';
|
||||||
|
|
||||||
|
describe('test comparisons', () => {
|
||||||
|
const frameA = toDataFrame({
|
||||||
|
fields: [
|
||||||
|
{ name: 'time', type: FieldType.time, values: [100, 200, 300] },
|
||||||
|
{ name: 'name', type: FieldType.string, values: ['a', 'b', 'c'] },
|
||||||
|
{ name: 'value', type: FieldType.number, values: [1, 2, 3] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const frameB = toDataFrame({
|
||||||
|
fields: [
|
||||||
|
{ name: 'time', type: FieldType.time, values: [100, 200, 300] },
|
||||||
|
{
|
||||||
|
name: 'value',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: [1, 2, 3],
|
||||||
|
config: {
|
||||||
|
decimals: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const field0 = frameB.fields[0];
|
||||||
|
const field1 = frameB.fields[1];
|
||||||
|
|
||||||
|
it('should support null/undefined without crash', () => {
|
||||||
|
expect(compareDataFrameStructures(frameA, frameA)).toBeTruthy();
|
||||||
|
expect(compareDataFrameStructures(frameA, { ...frameA })).toBeTruthy();
|
||||||
|
expect(compareDataFrameStructures(frameA, frameB)).toBeFalsy();
|
||||||
|
expect(compareDataFrameStructures(frameA, null as any)).toBeFalsy();
|
||||||
|
expect(compareDataFrameStructures(undefined as any, frameA)).toBeFalsy();
|
||||||
|
|
||||||
|
expect(compareArrayValues([frameA], [frameA], compareDataFrameStructures)).toBeTruthy();
|
||||||
|
expect(compareArrayValues([frameA], null as any, compareDataFrameStructures)).toBeFalsy();
|
||||||
|
expect(compareArrayValues(null as any, [frameA], compareDataFrameStructures)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('name change and field copy is not a structure change', () => {
|
||||||
|
expect(compareDataFrameStructures(frameB, { ...frameB, name: 'AA' })).toBeTruthy();
|
||||||
|
expect(compareDataFrameStructures(frameB, { ...frameB, fields: [field0, field1] })).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changing type should change the config', () => {
|
||||||
|
expect(
|
||||||
|
compareDataFrameStructures(frameB, {
|
||||||
|
...frameB,
|
||||||
|
fields: [
|
||||||
|
field0,
|
||||||
|
{
|
||||||
|
...field1,
|
||||||
|
type: FieldType.trace, // Change the type
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('full copy of config will not change structure', () => {
|
||||||
|
expect(
|
||||||
|
compareDataFrameStructures(frameB, {
|
||||||
|
...frameB,
|
||||||
|
fields: [
|
||||||
|
field0,
|
||||||
|
{
|
||||||
|
...field1,
|
||||||
|
config: {
|
||||||
|
...field1.config, // no change
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toBeTruthy(); // no change
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adding an additional config field', () => {
|
||||||
|
expect(
|
||||||
|
compareDataFrameStructures(frameB, {
|
||||||
|
...frameB,
|
||||||
|
fields: [
|
||||||
|
field0,
|
||||||
|
{
|
||||||
|
...field1,
|
||||||
|
config: {
|
||||||
|
...field1.config,
|
||||||
|
unit: 'rpm',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
67
packages/grafana-data/src/dataframe/frameComparisons.ts
Normal file
67
packages/grafana-data/src/dataframe/frameComparisons.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { DataFrame } from '../types/dataFrame';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if both frames have the same list of fields and configs.
|
||||||
|
* Field may have diferent names, labels and values but share the same structure
|
||||||
|
*
|
||||||
|
* To compare multiple frames use:
|
||||||
|
* ```
|
||||||
|
* areArraysEqual(a, b, framesHaveSameStructure);
|
||||||
|
* ```
|
||||||
|
* NOTE: this does a shallow check on the FieldConfig properties, when using the query
|
||||||
|
* editor, this should be sufficient, however if applicaitons are mutating properties
|
||||||
|
* deep in the FieldConfig this will not recognize a change
|
||||||
|
*
|
||||||
|
* @beta
|
||||||
|
*/
|
||||||
|
export function compareDataFrameStructures(a: DataFrame, b: DataFrame): boolean {
|
||||||
|
if (a === b) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a?.fields?.length !== b?.fields?.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < a.fields.length; i++) {
|
||||||
|
const fA = a.fields[i];
|
||||||
|
const fB = b.fields[i];
|
||||||
|
if (fA.type !== fB.type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const cfgA = fA.config as any;
|
||||||
|
const cfgB = fB.config as any;
|
||||||
|
|
||||||
|
const keys = Object.keys(cfgA);
|
||||||
|
if (keys.length !== Object.keys(cfgB).length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const key of keys) {
|
||||||
|
if (!cfgB.hasOwnProperty(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cfgA[key] !== cfgB[key]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all values in two arrays match the compare funciton
|
||||||
|
*
|
||||||
|
* @beta
|
||||||
|
*/
|
||||||
|
export function compareArrayValues<T>(a: T[], b: T[], cmp: (a: T, b: T) => boolean) {
|
||||||
|
if (a === b) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a?.length !== b?.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < a.length; i++) {
|
||||||
|
if (!cmp(a[i], b[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
@ -6,3 +6,4 @@ export * from './processDataFrame';
|
|||||||
export * from './dimensions';
|
export * from './dimensions';
|
||||||
export * from './ArrowDataFrame';
|
export * from './ArrowDataFrame';
|
||||||
export * from './ArrayDataFrame';
|
export * from './ArrayDataFrame';
|
||||||
|
export * from './frameComparisons';
|
||||||
|
@ -43,6 +43,5 @@ export const setGrafanaLiveSrv = (instance: GrafanaLiveSrv) => {
|
|||||||
* server side events and streams
|
* server side events and streams
|
||||||
*
|
*
|
||||||
* @alpha -- experimental
|
* @alpha -- experimental
|
||||||
* @public
|
|
||||||
*/
|
*/
|
||||||
export const getGrafanaLiveSrv = (): GrafanaLiveSrv => singletonInstance;
|
export const getGrafanaLiveSrv = (): GrafanaLiveSrv => singletonInstance;
|
||||||
|
Loading…
Reference in New Issue
Block a user