浏览代码

Feat: Angular panels & SeriesData to Table/TimeSeries (#16266)

Similar to how the react panels make sure all data is SeriesData before passing it to the react panels, this makes sure SeriesData is converted to TableData|TimeSeries before passing it to the angular panels.

It also changes the typing to encourage using SeriesData for future development
Ryan McKinley 6 年之前
父节点
当前提交
03951c00cb

+ 6 - 1
packages/grafana-ui/src/types/datasource.ts

@@ -2,7 +2,12 @@ import { TimeRange, RawTimeRange } from './time';
 import { PluginMeta } from './plugin';
 import { TableData, TimeSeries, SeriesData } from './data';
 
-export type DataQueryResponseData = TimeSeries | TableData | SeriesData | any;
+/**
+ * Starting in v6.2 SeriesData can represent both TimeSeries and TableData
+ */
+export type LegacyResponseData = TimeSeries | TableData | any;
+
+export type DataQueryResponseData = SeriesData | LegacyResponseData;
 
 export interface DataQueryResponse {
   data: DataQueryResponseData[];

+ 39 - 2
packages/grafana-ui/src/utils/processSeriesData.test.ts

@@ -1,5 +1,12 @@
-import { toSeriesData, guessFieldTypes, guessFieldTypeFromValue } from './processSeriesData';
-import { FieldType } from '../types/data';
+import {
+  isSeriesData,
+  toLegacyResponseData,
+  isTableData,
+  toSeriesData,
+  guessFieldTypes,
+  guessFieldTypeFromValue,
+} from './processSeriesData';
+import { FieldType, TimeSeries } from '../types/data';
 import moment from 'moment';
 
 describe('toSeriesData', () => {
@@ -63,3 +70,33 @@ describe('toSeriesData', () => {
     expect(norm.fields[3].type).toBe(FieldType.time); // based on name
   });
 });
+
+describe('SerisData backwards compatibility', () => {
+  it('converts TimeSeries to series and back again', () => {
+    const timeseries = {
+      target: 'Field Name',
+      datapoints: [[100, 1], [200, 2]],
+    };
+    const series = toSeriesData(timeseries);
+    expect(isSeriesData(timeseries)).toBeFalsy();
+    expect(isSeriesData(series)).toBeTruthy();
+
+    const roundtrip = toLegacyResponseData(series) as TimeSeries;
+    expect(isSeriesData(roundtrip)).toBeFalsy();
+    expect(roundtrip.target).toBe(timeseries.target);
+  });
+
+  it('converts TableData to series and back again', () => {
+    const table = {
+      columns: [{ text: 'a', unit: 'ms' }, { text: 'b', unit: 'zz' }, { text: 'c', unit: 'yy' }],
+      rows: [[100, 1, 'a'], [200, 2, 'a']],
+    };
+    const series = toSeriesData(table);
+    expect(isTableData(table)).toBeTruthy();
+    expect(isSeriesData(series)).toBeTruthy();
+
+    const roundtrip = toLegacyResponseData(series) as TimeSeries;
+    expect(isTableData(roundtrip)).toBeTruthy();
+    expect(roundtrip).toMatchObject(table);
+  });
+});

+ 26 - 0
packages/grafana-ui/src/utils/processSeriesData.ts

@@ -157,6 +157,32 @@ export const toSeriesData = (data: any): SeriesData => {
   throw new Error('Unsupported data format');
 };
 
+export const toLegacyResponseData = (series: SeriesData): TimeSeries | TableData => {
+  const { fields, rows } = series;
+
+  if (fields.length === 2) {
+    const type = guessFieldTypeFromTable(series, 1);
+    if (type === FieldType.time) {
+      return {
+        target: fields[0].name || series.name,
+        datapoints: rows,
+        unit: fields[0].unit,
+      } as TimeSeries;
+    }
+  }
+
+  return {
+    columns: fields.map(f => {
+      return {
+        text: f.name,
+        filterable: f.filterable,
+        unit: f.unit,
+      };
+    }),
+    rows,
+  };
+};
+
 export function sortSeriesData(data: SeriesData, sortIndex?: number, reverse = false): SeriesData {
   if (isNumber(sortIndex)) {
     const copy = {

+ 9 - 1
public/app/features/panel/metrics_panel_ctrl.ts

@@ -6,6 +6,7 @@ import { PanelCtrl } from 'app/features/panel/panel_ctrl';
 import { getExploreUrl } from 'app/core/utils/explore';
 import { applyPanelTimeOverrides, getResolution } from 'app/features/dashboard/utils/panel';
 import { ContextSrv } from 'app/core/services/context_srv';
+import { toLegacyResponseData, isSeriesData } from '@grafana/ui';
 
 class MetricsPanelCtrl extends PanelCtrl {
   scope: any;
@@ -188,7 +189,14 @@ class MetricsPanelCtrl extends PanelCtrl {
       result = { data: [] };
     }
 
-    this.events.emit('data-received', result.data);
+    // Make sure the data is TableData | TimeSeries
+    const data = result.data.map(v => {
+      if (isSeriesData(v)) {
+        return toLegacyResponseData(v);
+      }
+      return v;
+    });
+    this.events.emit('data-received', data);
   }
 
   handleDataStream(stream) {