mirror of https://github.com/grafana/grafana.git
use TableData for timeseries in react
This commit is contained in:
parent
56682cb1eb
commit
abf015ace2
|
|
@ -53,12 +53,9 @@ export interface TimeSeriesVMs {
|
||||||
length: number;
|
length: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Column {
|
export interface Column {
|
||||||
text: string;
|
text: string; // name
|
||||||
title?: string;
|
type?: 'time' | 'number' | 'string' | 'object';
|
||||||
type?: string;
|
|
||||||
sort?: boolean;
|
|
||||||
desc?: boolean;
|
|
||||||
filterable?: boolean;
|
filterable?: boolean;
|
||||||
unit?: string;
|
unit?: string;
|
||||||
}
|
}
|
||||||
|
|
@ -67,5 +64,4 @@ export interface TableData {
|
||||||
columns: Column[];
|
columns: Column[];
|
||||||
rows: any[];
|
rows: any[];
|
||||||
type: string;
|
type: string;
|
||||||
columnMap: any;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { ComponentClass } from 'react';
|
import { ComponentClass } from 'react';
|
||||||
import { TimeSeries, LoadingState, TableData } from './data';
|
import { LoadingState, TableData } from './data';
|
||||||
import { TimeRange } from './time';
|
import { TimeRange } from './time';
|
||||||
import { ScopedVars } from './datasource';
|
import { ScopedVars } from './datasource';
|
||||||
|
|
||||||
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
|
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
|
||||||
|
|
||||||
export interface PanelProps<T = any> {
|
export interface PanelProps<T = any> {
|
||||||
panelData: PanelData;
|
data?: TableData[];
|
||||||
timeRange: TimeRange;
|
timeRange: TimeRange;
|
||||||
loading: LoadingState;
|
loading: LoadingState;
|
||||||
options: T;
|
options: T;
|
||||||
|
|
@ -16,11 +16,6 @@ export interface PanelProps<T = any> {
|
||||||
replaceVariables: InterpolateFunction;
|
replaceVariables: InterpolateFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PanelData {
|
|
||||||
timeSeries?: TimeSeries[];
|
|
||||||
tableData?: TableData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PanelEditorProps<T = any> {
|
export interface PanelEditorProps<T = any> {
|
||||||
options: T;
|
options: T;
|
||||||
onOptionsChange: (options: T) => void;
|
onOptionsChange: (options: T) => void;
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,19 @@ import isNumber from 'lodash/isNumber';
|
||||||
import { colors } from './colors';
|
import { colors } from './colors';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { TimeSeries, TimeSeriesVMs, NullValueMode, TimeSeriesValue } from '../types';
|
import { TimeSeriesVMs, NullValueMode, TimeSeriesValue, TableData } from '../types';
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
timeSeries: TimeSeries[];
|
data: TableData[];
|
||||||
|
xColumn: number; // Time
|
||||||
|
yColumn: number; // Value
|
||||||
nullValueMode: NullValueMode;
|
nullValueMode: NullValueMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeSeriesVMs {
|
export function processTimeSeries({ data, xColumn, yColumn, nullValueMode }: Options): TimeSeriesVMs {
|
||||||
const vmSeries = timeSeries.map((item, index) => {
|
const vmSeries = data.map((item, index) => {
|
||||||
const colorIndex = index % colors.length;
|
const colorIndex = index % colors.length;
|
||||||
const label = item.target;
|
const label = item.columns[yColumn].text;
|
||||||
const result = [];
|
const result = [];
|
||||||
|
|
||||||
// stat defaults
|
// stat defaults
|
||||||
|
|
@ -42,9 +44,9 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS
|
||||||
let previousValue = 0;
|
let previousValue = 0;
|
||||||
let previousDeltaUp = true;
|
let previousDeltaUp = true;
|
||||||
|
|
||||||
for (let i = 0; i < item.datapoints.length; i++) {
|
for (let i = 0; i < item.rows.length; i++) {
|
||||||
currentValue = item.datapoints[i][0];
|
currentValue = item.rows[i][yColumn];
|
||||||
currentTime = item.datapoints[i][1];
|
currentTime = item.rows[i][xColumn];
|
||||||
|
|
||||||
if (typeof currentTime !== 'number') {
|
if (typeof currentTime !== 'number') {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -95,7 +97,7 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS
|
||||||
if (previousValue > currentValue) {
|
if (previousValue > currentValue) {
|
||||||
// counter reset
|
// counter reset
|
||||||
previousDeltaUp = false;
|
previousDeltaUp = false;
|
||||||
if (i === item.datapoints.length - 1) {
|
if (i === item.rows.length - 1) {
|
||||||
// reset on last
|
// reset on last
|
||||||
delta += currentValue;
|
delta += currentValue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,15 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { Column, TableData } from '@grafana/ui';
|
||||||
|
|
||||||
interface Column {
|
// This class mutates and uses the extra column fields
|
||||||
text: string;
|
interface ColumnEX extends Column {
|
||||||
title?: string;
|
title?: string;
|
||||||
type?: string;
|
|
||||||
sort?: boolean;
|
sort?: boolean;
|
||||||
desc?: boolean;
|
desc?: boolean;
|
||||||
filterable?: boolean;
|
|
||||||
unit?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TableModel {
|
export default class TableModel implements TableData {
|
||||||
columns: Column[];
|
columns: ColumnEX[];
|
||||||
rows: any[];
|
rows: any[];
|
||||||
type: string;
|
type: string;
|
||||||
columnMap: any;
|
columnMap: any;
|
||||||
|
|
|
||||||
|
|
@ -11,16 +11,16 @@ import {
|
||||||
DataQueryResponse,
|
DataQueryResponse,
|
||||||
DataQueryError,
|
DataQueryError,
|
||||||
LoadingState,
|
LoadingState,
|
||||||
PanelData,
|
|
||||||
TableData,
|
TableData,
|
||||||
TimeRange,
|
TimeRange,
|
||||||
TimeSeries,
|
|
||||||
ScopedVars,
|
ScopedVars,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
|
|
||||||
|
import { toTableData } from '../utils/panel';
|
||||||
|
|
||||||
interface RenderProps {
|
interface RenderProps {
|
||||||
loading: LoadingState;
|
loading: LoadingState;
|
||||||
panelData: PanelData;
|
data: TableData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
|
@ -44,7 +44,7 @@ export interface State {
|
||||||
isFirstLoad: boolean;
|
isFirstLoad: boolean;
|
||||||
loading: LoadingState;
|
loading: LoadingState;
|
||||||
response: DataQueryResponse;
|
response: DataQueryResponse;
|
||||||
panelData: PanelData;
|
data?: TableData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DataPanel extends Component<Props, State> {
|
export class DataPanel extends Component<Props, State> {
|
||||||
|
|
@ -64,7 +64,6 @@ export class DataPanel extends Component<Props, State> {
|
||||||
response: {
|
response: {
|
||||||
data: [],
|
data: [],
|
||||||
},
|
},
|
||||||
panelData: {},
|
|
||||||
isFirstLoad: true,
|
isFirstLoad: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -146,10 +145,12 @@ export class DataPanel extends Component<Props, State> {
|
||||||
onDataResponse(resp);
|
onDataResponse(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = toTableData(resp.data);
|
||||||
|
console.log('Converted:', data);
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: LoadingState.Done,
|
loading: LoadingState.Done,
|
||||||
response: resp,
|
response: resp,
|
||||||
panelData: this.getPanelData(resp),
|
data,
|
||||||
isFirstLoad: false,
|
isFirstLoad: false,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -172,23 +173,9 @@ export class DataPanel extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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() {
|
render() {
|
||||||
const { queries } = this.props;
|
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
|
// do not render component until we have first data
|
||||||
if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) {
|
if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) {
|
||||||
|
|
@ -203,10 +190,12 @@ export class DataPanel extends Component<Props, State> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('RENDER', data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{loading === LoadingState.Loading && this.renderLoadingState()}
|
{loading === LoadingState.Loading && this.renderLoadingState()}
|
||||||
{this.props.children({ loading, panelData })}
|
{this.props.children({ loading, data })}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import { profiler } from 'app/core/profiler';
|
||||||
// Types
|
// Types
|
||||||
import { DashboardModel, PanelModel } from '../state';
|
import { DashboardModel, PanelModel } from '../state';
|
||||||
import { PanelPlugin } from 'app/types';
|
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 { ScopedVars } from '@grafana/ui';
|
||||||
|
|
||||||
import variables from 'sass/_variables.generated.scss';
|
import variables from 'sass/_variables.generated.scss';
|
||||||
|
|
@ -142,7 +142,7 @@ export class PanelChrome extends PureComponent<Props, State> {
|
||||||
return this.hasPanelSnapshot ? snapshotDataToPanelData(this.props.panel) : null;
|
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 { panel, plugin } = this.props;
|
||||||
const { timeRange, renderCounter } = this.state;
|
const { timeRange, renderCounter } = this.state;
|
||||||
const PanelComponent = plugin.exports.reactPanel.panel;
|
const PanelComponent = plugin.exports.reactPanel.panel;
|
||||||
|
|
@ -157,7 +157,7 @@ export class PanelChrome extends PureComponent<Props, State> {
|
||||||
<div className="panel-content">
|
<div className="panel-content">
|
||||||
<PanelComponent
|
<PanelComponent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
panelData={panelData}
|
data={data}
|
||||||
timeRange={timeRange}
|
timeRange={timeRange}
|
||||||
options={panel.getOptions(plugin.exports.reactPanel.defaults)}
|
options={panel.getOptions(plugin.exports.reactPanel.defaults)}
|
||||||
width={width - 2 * variables.panelhorizontalpadding}
|
width={width - 2 * variables.panelhorizontalpadding}
|
||||||
|
|
@ -188,8 +188,8 @@ export class PanelChrome extends PureComponent<Props, State> {
|
||||||
onDataResponse={this.onDataResponse}
|
onDataResponse={this.onDataResponse}
|
||||||
onError={this.onDataError}
|
onError={this.onDataError}
|
||||||
>
|
>
|
||||||
{({ loading, panelData }) => {
|
{({ loading, data }) => {
|
||||||
return this.renderPanelPlugin(loading, panelData, width, height);
|
return this.renderPanelPlugin(loading, data, width, height);
|
||||||
}}
|
}}
|
||||||
</DataPanel>
|
</DataPanel>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ import store from 'app/core/store';
|
||||||
// Models
|
// Models
|
||||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||||
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
|
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
|
||||||
import { PanelData, TimeRange, TimeSeries } from '@grafana/ui';
|
import { TableData, TimeRange, TimeSeries } from '@grafana/ui';
|
||||||
import { TableData } from '@grafana/ui/src';
|
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import { isString as _isString } from 'lodash';
|
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 isTimeSeries = (data: any): data is TimeSeries => data && data.hasOwnProperty('datapoints');
|
||||||
const isTableData = (data: any): data is TableData => data && data.hasOwnProperty('columns');
|
const isTableData = (data: any): data is TableData => data && data.hasOwnProperty('columns');
|
||||||
export const snapshotDataToPanelData = (panel: PanelModel): PanelData => {
|
export const snapshotDataToPanelData = (panel: PanelModel): TableData[] => {
|
||||||
const snapshotData = panel.snapshotData;
|
return toTableData(panel.snapshotData);
|
||||||
if (isTimeSeries(snapshotData[0])) {
|
};
|
||||||
return {
|
|
||||||
timeSeries: snapshotData,
|
export const toTableData = (results: any[]): TableData[] => {
|
||||||
} as PanelData;
|
if (!results) {
|
||||||
} else if (isTableData(snapshotData[0])) {
|
return [];
|
||||||
return {
|
}
|
||||||
tableData: snapshotData[0],
|
return results.map(data => {
|
||||||
} as PanelData;
|
if (isTableData(data)) {
|
||||||
}
|
return data as TableData;
|
||||||
throw new Error('snapshotData is invalid:' + snapshotData.toString());
|
}
|
||||||
|
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');
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,29 +22,39 @@ export class GaugePanel extends Component<Props, State> {
|
||||||
this.state = {
|
this.state = {
|
||||||
value: this.findValue(props),
|
value: this.findValue(props),
|
||||||
};
|
};
|
||||||
|
console.log('CONSTRUCTOR!', this.props.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
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) });
|
this.setState({ value: this.findValue(this.props) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findValue(props: Props): number | null {
|
findValue(props: Props): number | null {
|
||||||
const { panelData, options } = props;
|
const { data, options } = props;
|
||||||
const { valueOptions } = options;
|
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({
|
const vmSeries = processTimeSeries({
|
||||||
timeSeries: panelData.timeSeries,
|
data,
|
||||||
|
xColumn,
|
||||||
|
yColumn,
|
||||||
nullValueMode: NullValueMode.Null,
|
nullValueMode: NullValueMode.Null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('GOT', vmSeries);
|
||||||
|
|
||||||
if (vmSeries[0]) {
|
if (vmSeries[0]) {
|
||||||
return vmSeries[0].stats[valueOptions.stat];
|
return vmSeries[0].stats[valueOptions.stat];
|
||||||
}
|
}
|
||||||
} else if (panelData.tableData) {
|
|
||||||
return panelData.tableData.rows[0].find(prop => prop > 0);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,18 @@ interface Props extends PanelProps<Options> {}
|
||||||
|
|
||||||
export class GraphPanel extends PureComponent<Props> {
|
export class GraphPanel extends PureComponent<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { panelData, timeRange, width, height } = this.props;
|
const { data, timeRange, width, height } = this.props;
|
||||||
const { showLines, showBars, showPoints } = this.props.options;
|
const { showLines, showBars, showPoints } = this.props.options;
|
||||||
|
|
||||||
let vmSeries: TimeSeriesVMs;
|
let vmSeries: TimeSeriesVMs;
|
||||||
if (panelData.timeSeries) {
|
if (data) {
|
||||||
|
// For now, assume timeseries defaults
|
||||||
|
const xColumn = 1; // time
|
||||||
|
const yColumn = 0; // value
|
||||||
vmSeries = processTimeSeries({
|
vmSeries = processTimeSeries({
|
||||||
timeSeries: panelData.timeSeries,
|
data,
|
||||||
|
xColumn,
|
||||||
|
yColumn,
|
||||||
nullValueMode: NullValueMode.Ignore,
|
nullValueMode: NullValueMode.Ignore,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue