DataFrame: Deprecate MutableDataFrame (#67569)

This commit is contained in:
Ryan McKinley
2023-05-01 19:41:40 -07:00
committed by GitHub
parent d31d1576fb
commit 86302bc1ec
35 changed files with 266 additions and 219 deletions
@@ -10,10 +10,16 @@ import { guessFieldTypeFromValue, guessFieldTypeForField, toDataFrameDTO } from
/** @deprecated */
export type MutableField<T = any> = Field<T>;
/** @deprecated */
type MutableVectorCreator = (buffer?: any[]) => any[];
export const MISSING_VALUE = undefined; // Treated as connected in new graph panel
/**
* MutableDataFrame is a complex wrapper around the DataFrame interface
*
* @deprecated use standard DataFrame, or create one with PartialDataFrame
*/
export class MutableDataFrame<T = any> extends FunctionalVector<T> implements DataFrame {
name?: string;
refId?: string;
@@ -2,8 +2,8 @@ import { dateTime } from '../datetime/moment_wrapper';
import { DataFrameDTO, FieldType, TableData, TimeSeries } from '../types/index';
import { ArrayDataFrame } from './ArrayDataFrame';
import { MutableDataFrame } from './MutableDataFrame';
import {
createDataFrame,
guessFieldTypeFromValue,
guessFieldTypes,
isDataFrame,
@@ -130,7 +130,7 @@ describe('toDataFrame', () => {
});
it('Guess Column Types from series', () => {
const series = new MutableDataFrame({
const series = createDataFrame({
fields: [
{ name: 'A (number)', values: [123, null] },
{ name: 'B (strings)', values: [null, 'Hello'] },
@@ -27,7 +27,6 @@ import {
import { arrayToDataFrame } from './ArrayDataFrame';
import { dataFrameFromJSON } from './DataFrameJSON';
import { MutableDataFrame } from './MutableDataFrame';
function convertTableToDataFrame(table: TableData): DataFrame {
const fields = table.columns.map((c) => {
@@ -315,7 +314,7 @@ export function toDataFrame(data: any): DataFrame {
}
// This will convert the array values into Vectors
return new MutableDataFrame(data as DataFrameDTO);
return createDataFrame(data as DataFrameDTO);
}
// Handle legacy docs/json type
@@ -567,3 +566,35 @@ export function preProcessPanelData(data: PanelData, lastResult?: PanelData): Pa
timings: { dataProcessingTime: STOPTIME - STARTTIME },
};
}
export interface PartialDataFrame extends Omit<DataFrame, 'fields' | 'length'> {
fields: Array<Partial<Field>>;
}
export function createDataFrame(input: PartialDataFrame): DataFrame {
let length = 0;
const fields = input.fields.map((p, idx) => {
const { state, ...field } = p;
if (!field.name) {
field.name = `Field ${idx + 1}`;
}
if (!field.config) {
field.config = {};
}
if (!field.values) {
field.values = new Array(length);
} else if (field.values.length > length) {
length = field.values.length;
}
if (!field.type) {
field.type = guessFieldTypeForField(field as Field) ?? FieldType.other;
}
return field as Field;
});
return {
...input,
fields,
length,
};
}
@@ -508,8 +508,25 @@ function createEmptyDisplayOptions(extend = {}): GetFieldDisplayValuesOptions {
}
function createDisplayOptions(extend: Partial<GetFieldDisplayValuesOptions> = {}): GetFieldDisplayValuesOptions {
const options: GetFieldDisplayValuesOptions = {
data: [
const options = merge(
{
replaceVariables: (value: string) => {
return value;
},
reduceOptions: {
calcs: [],
},
fieldConfig: {
overrides: [],
defaults: {},
},
theme: createTheme(),
},
extend
);
if (!options.data?.length) {
options.data = [
toDataFrame({
name: 'Series Name',
fields: [
@@ -518,21 +535,9 @@ function createDisplayOptions(extend: Partial<GetFieldDisplayValuesOptions> = {}
{ name: 'Field 3', values: [2, 4, 6] },
],
}),
],
replaceVariables: (value: string) => {
return value;
},
reduceOptions: {
calcs: [],
},
fieldConfig: {
overrides: [],
defaults: {},
},
theme: createTheme(),
};
return merge(options, extend);
];
}
return options;
}
describe('fixCellTemplateExpressions', () => {
@@ -1,4 +1,4 @@
import { ArrayDataFrame, MutableDataFrame, toDataFrame } from '../dataframe';
import { ArrayDataFrame, createDataFrame, toDataFrame } from '../dataframe';
import { rangeUtil } from '../datetime';
import { createTheme } from '../themes';
import { FieldMatcherID } from '../transformations';
@@ -177,11 +177,11 @@ describe('applyFieldOverrides', () => {
};
describe('given multiple data frames', () => {
const f0 = new MutableDataFrame({
const f0 = createDataFrame({
name: 'A',
fields: [{ name: 'message', type: FieldType.string, values: [10, 20] }],
});
const f1 = new MutableDataFrame({
const f1 = createDataFrame({
name: 'B',
fields: [{ name: 'info', type: FieldType.string, values: [10, 20] }],
});
@@ -676,7 +676,7 @@ describe('getLinksSupplier', () => {
getTimeRangeForUrl: () => ({ from: 'now-7d', to: 'now' }),
});
const f0 = new MutableDataFrame({
const f0 = createDataFrame({
name: 'A',
fields: [
{
@@ -712,7 +712,7 @@ describe('getLinksSupplier', () => {
});
const datasourceUid = '1234';
const f0 = new MutableDataFrame({
const f0 = createDataFrame({
name: 'A',
fields: [
{
@@ -766,7 +766,7 @@ describe('getLinksSupplier', () => {
const datasourceUid = '1234';
const range = rangeUtil.relativeToTimeRange({ from: 600, to: 0 });
const f0 = new MutableDataFrame({
const f0 = createDataFrame({
name: 'A',
fields: [
{
@@ -827,7 +827,7 @@ describe('getLinksSupplier', () => {
it('handles link click handlers', () => {
const onClickSpy = jest.fn();
const replaceSpy = jest.fn();
const f0 = new MutableDataFrame({
const f0 = createDataFrame({
name: 'A',
fields: [
{
@@ -867,7 +867,7 @@ describe('getLinksSupplier', () => {
const replaceSpy = jest.fn().mockReturnValue('url interpolated 10');
const onBuildUrlSpy = jest.fn();
const f0 = new MutableDataFrame({
const f0 = createDataFrame({
name: 'A',
fields: [
{
@@ -1,4 +1,4 @@
import { MutableDataFrame, toDataFrame } from '../dataframe';
import { createDataFrame, toDataFrame } from '../dataframe';
import { createTheme } from '../themes';
import { applyFieldOverrides } from './fieldOverrides';
@@ -85,7 +85,7 @@ describe('getFieldDisplayValuesProxy', () => {
it('should use default display processor if display is not defined', () => {
const p = getFieldDisplayValuesProxy({
frame: new MutableDataFrame({ fields: [{ name: 'test', values: [1, 2] }] }),
frame: createDataFrame({ fields: [{ name: 'test', values: [1, 2] }] }),
rowIndex: 0,
});
expect(p.test.text).toBe('1');
@@ -1,7 +1,6 @@
import { difference } from 'lodash';
import { MutableDataFrame } from '../dataframe/MutableDataFrame';
import { guessFieldTypeFromValue } from '../dataframe/processDataFrame';
import { createDataFrame, guessFieldTypeFromValue } from '../dataframe/processDataFrame';
import { Field, FieldType } from '../types/index';
import { fieldReducers, ReducerID, reduceField } from './fieldReducer';
@@ -24,7 +23,7 @@ function createField<T>(name: string, values?: T[], type?: FieldType): Field<T>
}
describe('Stats Calculators', () => {
const basicTable = new MutableDataFrame({
const basicTable = createDataFrame({
fields: [
{ name: 'a', values: [10, 20] },
{ name: 'b', values: [20, 30] },
@@ -1,6 +1,5 @@
import { map } from 'rxjs/operators';
import { MutableDataFrame } from '../../dataframe';
import { getFieldDisplayName } from '../../field/fieldState';
import { DataFrame, DataTransformerInfo, Field, FieldType, SpecialValue } from '../../types';
import { fieldMatchers } from '../matchers';
@@ -75,13 +74,14 @@ export const groupingToMatrixTransformer: DataTransformerInfo<GroupingToMatrixTr
matrixValues[columnName][rowName] = value;
}
const resultFrame = new MutableDataFrame();
resultFrame.addField({
name: rowColumnField,
values: rowValues,
type: FieldType.string,
});
const fields: Field[] = [
{
name: rowColumnField,
values: rowValues,
type: FieldType.string,
config: {},
},
];
for (const columnName of columnValues) {
let values = [];
@@ -98,7 +98,7 @@ export const groupingToMatrixTransformer: DataTransformerInfo<GroupingToMatrixTr
valueField.config = { ...valueField.config, displayNameFromDS: undefined };
}
resultFrame.addField({
fields.push({
name: columnName.toString(),
values: values,
config: valueField.config,
@@ -106,7 +106,12 @@ export const groupingToMatrixTransformer: DataTransformerInfo<GroupingToMatrixTr
});
}
return [resultFrame];
return [
{
fields,
length: rowValues.length,
},
];
})
),
};
@@ -1,11 +1,11 @@
import { MutableDataFrame } from '../dataframe/MutableDataFrame';
import { createDataFrame } from '../dataframe';
import { dateTime } from '../datetime/moment_wrapper';
import { TimeRange } from '../types/time';
import { getFlotPairs, getFlotPairsConstant } from './flotPairs';
describe('getFlotPairs', () => {
const series = new MutableDataFrame({
const series = createDataFrame({
fields: [
{ name: 'a', values: [1, 2, 3] },
{ name: 'b', values: [100, 200, 300] },
@@ -8,7 +8,7 @@ import {
DataSourceInstanceSettings,
DataSourceJsonData,
DataSourceRef,
MutableDataFrame,
createDataFrame,
} from '@grafana/data';
import {
@@ -240,10 +240,12 @@ describe('DataSourceWithBackend', () => {
let obs = toStreamingDataResponse(rsp, request, standardStreamOptionsProvider);
expect(obs).toBeDefined();
let frame = new MutableDataFrame();
frame.meta = {
channel: 'a/b/c',
};
let frame = createDataFrame({
meta: {
channel: 'a/b/c',
},
fields: [],
});
rsp.data = [frame];
obs = toStreamingDataResponse(rsp, request, standardStreamOptionsProvider);
expect(obs).toBeDefined();
@@ -1,4 +1,4 @@
import { FieldType, MutableDataFrame } from '@grafana/data';
import { FieldType, createDataFrame } from '@grafana/data';
import { applyNullInsertThreshold } from './nullInsertThreshold';
@@ -48,7 +48,7 @@ function genFrame() {
describe('nullInsertThreshold Transformer', () => {
test('should insert nulls at +threshold between adjacent > threshold: 1', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [1, 3, 10] },
@@ -65,7 +65,7 @@ describe('nullInsertThreshold Transformer', () => {
});
test('should insert nulls at +threshold between adjacent > threshold: 2', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [5, 7, 11] },
@@ -82,7 +82,7 @@ describe('nullInsertThreshold Transformer', () => {
});
test('should insert nulls at +interval between adjacent > interval: 1', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [1, 3, 10] },
@@ -99,7 +99,7 @@ describe('nullInsertThreshold Transformer', () => {
});
test('should insert leading null at beginning +interval when timeRange.from.valueOf() exceeds threshold', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [4, 6, 13] },
@@ -154,7 +154,7 @@ describe('nullInsertThreshold Transformer', () => {
// this tests that intervals at 24hr but starting not at 12am UTC are not always snapped to 12am UTC
test('should insert leading null at beginning +interval when timeRange.from.valueOf() exceeds threshold 11PM UTC', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{
@@ -181,7 +181,7 @@ describe('nullInsertThreshold Transformer', () => {
});
test('should insert trailing null at end +interval when timeRange.to.valueOf() exceeds threshold', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [1, 3, 10] },
@@ -197,7 +197,7 @@ describe('nullInsertThreshold Transformer', () => {
expect(result.fields[2].values).toEqual(['a', null, 'b', null, null, null, null, null, null, 'c', null, null]);
// should work for frames with 1 datapoint
const df2 = new MutableDataFrame({
const df2 = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [1] },
@@ -216,7 +216,7 @@ describe('nullInsertThreshold Transformer', () => {
});
test('should not insert trailing null at end +interval when timeRange.to.valueOf() equals threshold', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [1] },
@@ -234,7 +234,7 @@ describe('nullInsertThreshold Transformer', () => {
// TODO: make this work
test.skip('should insert nulls at +threshold (when defined) instead of +interval', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 2 }, values: [5, 7, 11] },
@@ -251,7 +251,7 @@ describe('nullInsertThreshold Transformer', () => {
});
test('should noop on 0 datapoints', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [] },
@@ -265,7 +265,7 @@ describe('nullInsertThreshold Transformer', () => {
});
test('should noop on invalid threshold', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [1, 2, 4] },
@@ -279,7 +279,7 @@ describe('nullInsertThreshold Transformer', () => {
});
test('should noop on invalid interval', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: -1 }, values: [1, 2, 4] },
@@ -293,7 +293,7 @@ describe('nullInsertThreshold Transformer', () => {
});
test('should noop when no missing steps', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [1, 2, 3] },
@@ -307,7 +307,7 @@ describe('nullInsertThreshold Transformer', () => {
});
test('should noop when refFieldName not found', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [1, 2, 5] },
@@ -1,11 +1,11 @@
import { FieldType, MutableDataFrame } from '@grafana/data';
import { FieldType, createDataFrame } from '@grafana/data';
import { applyNullInsertThreshold } from './nullInsertThreshold';
import { nullToValue } from './nullToValue';
describe('nullToValue Transformer', () => {
test('should change all nulls to configured zero value', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [1, 3, 10] },
@@ -32,7 +32,7 @@ describe('nullToValue Transformer', () => {
});
test('should change all nulls to configured positive value', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, values: [5, 7, 11] },
@@ -59,7 +59,7 @@ describe('nullToValue Transformer', () => {
});
test('should change all nulls to configured negative value', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [1, 3, 10] },
@@ -76,7 +76,7 @@ describe('nullToValue Transformer', () => {
});
test('should have no effect without nulls', () => {
const df = new MutableDataFrame({
const df = createDataFrame({
refId: 'A',
fields: [
{ name: 'Time', type: FieldType.time, config: { interval: 1 }, values: [1, 2, 3] },