From abf015ace227a113fcda67542f2aa77c7f348e66 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 7 Mar 2019 12:13:38 -0800 Subject: [PATCH] use TableData for timeseries in react --- packages/grafana-ui/src/types/data.ts | 10 ++-- packages/grafana-ui/src/types/panel.ts | 9 +--- .../grafana-ui/src/utils/processTimeSeries.ts | 20 ++++---- public/app/core/table_model.ts | 12 ++--- .../features/dashboard/dashgrid/DataPanel.tsx | 33 +++++-------- .../dashboard/dashgrid/PanelChrome.tsx | 10 ++-- public/app/features/dashboard/utils/panel.ts | 46 +++++++++++++------ public/app/plugins/panel/gauge/GaugePanel.tsx | 22 ++++++--- .../app/plugins/panel/graph2/GraphPanel.tsx | 11 +++-- 9 files changed, 94 insertions(+), 79 deletions(-) diff --git a/packages/grafana-ui/src/types/data.ts b/packages/grafana-ui/src/types/data.ts index 1e4ccba3948..1ea89bcd28e 100644 --- a/packages/grafana-ui/src/types/data.ts +++ b/packages/grafana-ui/src/types/data.ts @@ -53,12 +53,9 @@ export interface TimeSeriesVMs { length: number; } -interface Column { - text: string; - title?: string; - type?: string; - sort?: boolean; - desc?: boolean; +export interface Column { + text: string; // name + type?: 'time' | 'number' | 'string' | 'object'; filterable?: boolean; unit?: string; } @@ -67,5 +64,4 @@ export interface TableData { columns: Column[]; rows: any[]; type: string; - columnMap: any; } diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 260ff78df76..4d9dc820b96 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -1,12 +1,12 @@ import { ComponentClass } from 'react'; -import { TimeSeries, LoadingState, TableData } from './data'; +import { LoadingState, TableData } from './data'; import { TimeRange } from './time'; import { ScopedVars } from './datasource'; export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string; export interface PanelProps { - panelData: PanelData; + data?: TableData[]; timeRange: TimeRange; loading: LoadingState; options: T; @@ -16,11 +16,6 @@ export interface PanelProps { replaceVariables: InterpolateFunction; } -export interface PanelData { - timeSeries?: TimeSeries[]; - tableData?: TableData; -} - export interface PanelEditorProps { options: T; onOptionsChange: (options: T) => void; diff --git a/packages/grafana-ui/src/utils/processTimeSeries.ts b/packages/grafana-ui/src/utils/processTimeSeries.ts index f5e9f96efba..d8827e567e3 100644 --- a/packages/grafana-ui/src/utils/processTimeSeries.ts +++ b/packages/grafana-ui/src/utils/processTimeSeries.ts @@ -4,17 +4,19 @@ import isNumber from 'lodash/isNumber'; import { colors } from './colors'; // Types -import { TimeSeries, TimeSeriesVMs, NullValueMode, TimeSeriesValue } from '../types'; +import { TimeSeriesVMs, NullValueMode, TimeSeriesValue, TableData } from '../types'; interface Options { - timeSeries: TimeSeries[]; + data: TableData[]; + xColumn: number; // Time + yColumn: number; // Value nullValueMode: NullValueMode; } -export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeSeriesVMs { - const vmSeries = timeSeries.map((item, index) => { +export function processTimeSeries({ data, xColumn, yColumn, nullValueMode }: Options): TimeSeriesVMs { + const vmSeries = data.map((item, index) => { const colorIndex = index % colors.length; - const label = item.target; + const label = item.columns[yColumn].text; const result = []; // stat defaults @@ -42,9 +44,9 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS let previousValue = 0; let previousDeltaUp = true; - for (let i = 0; i < item.datapoints.length; i++) { - currentValue = item.datapoints[i][0]; - currentTime = item.datapoints[i][1]; + for (let i = 0; i < item.rows.length; i++) { + currentValue = item.rows[i][yColumn]; + currentTime = item.rows[i][xColumn]; if (typeof currentTime !== 'number') { continue; @@ -95,7 +97,7 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS if (previousValue > currentValue) { // counter reset previousDeltaUp = false; - if (i === item.datapoints.length - 1) { + if (i === item.rows.length - 1) { // reset on last delta += currentValue; } diff --git a/public/app/core/table_model.ts b/public/app/core/table_model.ts index fa7170bed13..988c3b1992e 100644 --- a/public/app/core/table_model.ts +++ b/public/app/core/table_model.ts @@ -1,17 +1,15 @@ import _ from 'lodash'; +import { Column, TableData } from '@grafana/ui'; -interface Column { - text: string; +// This class mutates and uses the extra column fields +interface ColumnEX extends Column { title?: string; - type?: string; sort?: boolean; desc?: boolean; - filterable?: boolean; - unit?: string; } -export default class TableModel { - columns: Column[]; +export default class TableModel implements TableData { + columns: ColumnEX[]; rows: any[]; type: string; columnMap: any; diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index 09864d85960..c12462e2b49 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -11,16 +11,16 @@ import { DataQueryResponse, DataQueryError, LoadingState, - PanelData, TableData, TimeRange, - TimeSeries, ScopedVars, } from '@grafana/ui'; +import { toTableData } from '../utils/panel'; + interface RenderProps { loading: LoadingState; - panelData: PanelData; + data: TableData[]; } export interface Props { @@ -44,7 +44,7 @@ export interface State { isFirstLoad: boolean; loading: LoadingState; response: DataQueryResponse; - panelData: PanelData; + data?: TableData[]; } export class DataPanel extends Component { @@ -64,7 +64,6 @@ export class DataPanel extends Component { response: { data: [], }, - panelData: {}, isFirstLoad: true, }; } @@ -146,10 +145,12 @@ export class DataPanel extends Component { onDataResponse(resp); } + const data = toTableData(resp.data); + console.log('Converted:', data); this.setState({ loading: LoadingState.Done, response: resp, - panelData: this.getPanelData(resp), + data, isFirstLoad: false, }); } catch (err) { @@ -172,23 +173,9 @@ export class DataPanel extends Component { } }; - getPanelData(response: DataQueryResponse) { - if (response.data.length > 0 && (response.data[0] as TableData).type === 'table') { - return { - tableData: response.data[0] as TableData, - timeSeries: null, - }; - } - - return { - timeSeries: response.data as TimeSeries[], - tableData: null, - }; - } - render() { const { queries } = this.props; - const { loading, isFirstLoad, panelData } = this.state; + const { loading, isFirstLoad, data } = this.state; // do not render component until we have first data if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) { @@ -203,10 +190,12 @@ export class DataPanel extends Component { ); } + console.log('RENDER', data); + return ( <> {loading === LoadingState.Loading && this.renderLoadingState()} - {this.props.children({ loading, panelData })} + {this.props.children({ loading, data })} ); } diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 0a9d1d44ceb..0dd82b7dad9 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -18,7 +18,7 @@ import { profiler } from 'app/core/profiler'; // Types import { DashboardModel, PanelModel } from '../state'; import { PanelPlugin } from 'app/types'; -import { DataQueryResponse, TimeRange, LoadingState, PanelData, DataQueryError } from '@grafana/ui'; +import { DataQueryResponse, TimeRange, LoadingState, TableData, DataQueryError } from '@grafana/ui'; import { ScopedVars } from '@grafana/ui'; import variables from 'sass/_variables.generated.scss'; @@ -142,7 +142,7 @@ export class PanelChrome extends PureComponent { return this.hasPanelSnapshot ? snapshotDataToPanelData(this.props.panel) : null; } - renderPanelPlugin(loading: LoadingState, panelData: PanelData, width: number, height: number): JSX.Element { + renderPanelPlugin(loading: LoadingState, data: TableData[], width: number, height: number): JSX.Element { const { panel, plugin } = this.props; const { timeRange, renderCounter } = this.state; const PanelComponent = plugin.exports.reactPanel.panel; @@ -157,7 +157,7 @@ export class PanelChrome extends PureComponent {
{ onDataResponse={this.onDataResponse} onError={this.onDataError} > - {({ loading, panelData }) => { - return this.renderPanelPlugin(loading, panelData, width, height); + {({ loading, data }) => { + return this.renderPanelPlugin(loading, data, width, height); }} ) : ( diff --git a/public/app/features/dashboard/utils/panel.ts b/public/app/features/dashboard/utils/panel.ts index d14432cb2eb..3058e2b9db6 100644 --- a/public/app/features/dashboard/utils/panel.ts +++ b/public/app/features/dashboard/utils/panel.ts @@ -4,8 +4,7 @@ import store from 'app/core/store'; // Models import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; -import { PanelData, TimeRange, TimeSeries } from '@grafana/ui'; -import { TableData } from '@grafana/ui/src'; +import { TableData, TimeRange, TimeSeries } from '@grafana/ui'; // Utils import { isString as _isString } from 'lodash'; @@ -173,16 +172,37 @@ export function getResolution(panel: PanelModel): number { const isTimeSeries = (data: any): data is TimeSeries => data && data.hasOwnProperty('datapoints'); const isTableData = (data: any): data is TableData => data && data.hasOwnProperty('columns'); -export const snapshotDataToPanelData = (panel: PanelModel): PanelData => { - const snapshotData = panel.snapshotData; - if (isTimeSeries(snapshotData[0])) { - return { - timeSeries: snapshotData, - } as PanelData; - } else if (isTableData(snapshotData[0])) { - return { - tableData: snapshotData[0], - } as PanelData; +export const snapshotDataToPanelData = (panel: PanelModel): TableData[] => { + return toTableData(panel.snapshotData); +}; + +export const toTableData = (results: any[]): TableData[] => { + if (!results) { + return []; } - throw new Error('snapshotData is invalid:' + snapshotData.toString()); + return results.map(data => { + if (isTableData(data)) { + return data as TableData; + } + if (isTimeSeries(data)) { + const ts = data as TimeSeries; + return { + type: 'timeseries', + columns: [ + { + text: ts.target, + unit: ts.unit, + type: 'number', // Is this really true? + }, + { + text: 'time', + type: 'time', + }, + ], + rows: ts.datapoints, + } as TableData; + } + console.warn('Can not convert', data); + throw new Error('Unsupported data format'); + }); }; diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index b75d4a1c7f3..7913aa33eb0 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -22,29 +22,39 @@ export class GaugePanel extends Component { this.state = { value: this.findValue(props), }; + console.log('CONSTRUCTOR!', this.props.data); } componentDidUpdate(prevProps: Props) { - if (this.props.panelData !== prevProps.panelData) { + console.log('UPDATE', this.props.data); + + if (this.props.data !== prevProps.data) { this.setState({ value: this.findValue(this.props) }); } } findValue(props: Props): number | null { - const { panelData, options } = props; + const { data, options } = props; const { valueOptions } = options; - if (panelData.timeSeries) { + console.log('FIND VALUE', data); + + if (data) { + // For now, assume timeseries defaults + const xColumn = 1; // time + const yColumn = 0; // value const vmSeries = processTimeSeries({ - timeSeries: panelData.timeSeries, + data, + xColumn, + yColumn, nullValueMode: NullValueMode.Null, }); + console.log('GOT', vmSeries); + if (vmSeries[0]) { return vmSeries[0].stats[valueOptions.stat]; } - } else if (panelData.tableData) { - return panelData.tableData.rows[0].find(prop => prop > 0); } return null; } diff --git a/public/app/plugins/panel/graph2/GraphPanel.tsx b/public/app/plugins/panel/graph2/GraphPanel.tsx index f1fc2b43d51..c859b5f9cf3 100644 --- a/public/app/plugins/panel/graph2/GraphPanel.tsx +++ b/public/app/plugins/panel/graph2/GraphPanel.tsx @@ -16,13 +16,18 @@ interface Props extends PanelProps {} export class GraphPanel extends PureComponent { render() { - const { panelData, timeRange, width, height } = this.props; + const { data, timeRange, width, height } = this.props; const { showLines, showBars, showPoints } = this.props.options; let vmSeries: TimeSeriesVMs; - if (panelData.timeSeries) { + if (data) { + // For now, assume timeseries defaults + const xColumn = 1; // time + const yColumn = 0; // value vmSeries = processTimeSeries({ - timeSeries: panelData.timeSeries, + data, + xColumn, + yColumn, nullValueMode: NullValueMode.Ignore, }); }