Commit 88b59ae3 by Ryan McKinley Committed by GitHub

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
parent 01df8f17
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();
});
});
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 './ArrowDataFrame';
export * from './ArrayDataFrame';
export * from './frameComparisons';
......@@ -43,6 +43,5 @@ export const setGrafanaLiveSrv = (instance: GrafanaLiveSrv) => {
* server side events and streams
*
* @alpha -- experimental
* @public
*/
export const getGrafanaLiveSrv = (): GrafanaLiveSrv => singletonInstance;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment