From b9ff1923e74ec5531cf4b68847c973e40adac299 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Sat, 20 Apr 2019 12:26:49 -0700 Subject: [PATCH] QueryEditors: pass PanelData and filtered PanelData to each editor (#16692) --- packages/grafana-ui/src/types/datasource.ts | 5 +- .../panel_editor/QueryEditorRow.test.ts | 45 ++++++++++++++++++ .../dashboard/panel_editor/QueryEditorRow.tsx | 46 +++++++++++++++---- 3 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 public/app/features/dashboard/panel_editor/QueryEditorRow.test.ts diff --git a/packages/grafana-ui/src/types/datasource.ts b/packages/grafana-ui/src/types/datasource.ts index 13864232565..7dfa7bcea9c 100644 --- a/packages/grafana-ui/src/types/datasource.ts +++ b/packages/grafana-ui/src/types/datasource.ts @@ -2,6 +2,7 @@ import { ComponentClass } from 'react'; import { TimeRange } from './time'; import { PluginMeta } from './plugin'; import { TableData, TimeSeries, SeriesData } from './data'; +import { PanelData } from './panel'; export class DataSourcePlugin { DataSourceClass: DataSourceConstructor; @@ -140,8 +141,8 @@ export interface QueryEditorProps void; onChange: (value: TQuery) => void; - queryResponse?: SeriesData[]; - queryError?: DataQueryError; + panelData: PanelData; // The current panel data + queryResponse?: PanelData; // data filtered to only this query. Includes the error. } export enum DataSourceStatus { diff --git a/public/app/features/dashboard/panel_editor/QueryEditorRow.test.ts b/public/app/features/dashboard/panel_editor/QueryEditorRow.test.ts new file mode 100644 index 00000000000..ccddec2657b --- /dev/null +++ b/public/app/features/dashboard/panel_editor/QueryEditorRow.test.ts @@ -0,0 +1,45 @@ +import { PanelData, LoadingState, DataQueryRequest } from '@grafana/ui'; +import { filterPanelDataToQuery } from './QueryEditorRow'; + +function makePretendRequest(requestId: string, subRequests?: DataQueryRequest[]): DataQueryRequest { + return { + requestId, + // subRequests, + } as DataQueryRequest; +} + +describe('filterPanelDataToQuery', () => { + const data = { + state: LoadingState.Done, + series: [ + { refId: 'A', fields: [{ name: 'AAA' }], rows: [], meta: {} }, + { refId: 'B', fields: [{ name: 'B111' }], rows: [], meta: {} }, + { refId: 'B', fields: [{ name: 'B222' }], rows: [], meta: {} }, + { refId: 'B', fields: [{ name: 'B333' }], rows: [], meta: {} }, + { refId: 'C', fields: [{ name: 'CCCC' }], rows: [], meta: { requestId: 'sub3' } }, + ], + error: { + refId: 'B', + message: 'Error!!', + }, + request: makePretendRequest('111', [ + makePretendRequest('sub1'), + makePretendRequest('sub2'), + makePretendRequest('sub3'), + ]), + } as PanelData; + + it('should not have an error unless the refId matches', () => { + const panelData = filterPanelDataToQuery(data, 'A'); + expect(panelData.series.length).toBe(1); + expect(panelData.series[0].refId).toBe('A'); + expect(panelData.error).toBeUndefined(); + }); + + it('should match the error to the query', () => { + const panelData = filterPanelDataToQuery(data, 'B'); + expect(panelData.series.length).toBe(3); + expect(panelData.series[0].refId).toBe('B'); + expect(panelData.error!.refId).toBe('B'); + }); +}); diff --git a/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx b/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx index 06218dfcde2..8b5f6b964f2 100644 --- a/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx +++ b/public/app/features/dashboard/panel_editor/QueryEditorRow.tsx @@ -11,7 +11,7 @@ import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; // Types import { PanelModel } from '../state/PanelModel'; -import { DataQuery, DataSourceApi, TimeRange, DataQueryError, SeriesData, PanelData } from '@grafana/ui'; +import { DataQuery, DataSourceApi, TimeRange, PanelData, LoadingState, DataQueryRequest } from '@grafana/ui'; import { DashboardModel } from '../state/DashboardModel'; interface Props { @@ -32,8 +32,7 @@ interface State { datasource: DataSourceApi | null; isCollapsed: boolean; hasTextEditMode: boolean; - queryError: DataQueryError | null; - queryResponse: SeriesData[] | null; + queryResponse?: PanelData; } export class QueryEditorRow extends PureComponent { @@ -46,7 +45,6 @@ export class QueryEditorRow extends PureComponent { isCollapsed: false, loadedDataSourceValue: undefined, hasTextEditMode: false, - queryError: null, queryResponse: null, }; @@ -93,9 +91,7 @@ export class QueryEditorRow extends PureComponent { const { data, query } = this.props; if (data !== prevProps.data) { - const queryError = data.error && data.error.refId === query.refId ? data.error : null; - const queryResponse = data.series.filter(series => series.refId === query.refId); - this.setState({ queryResponse, queryError }); + this.setState({ queryResponse: filterPanelDataToQuery(data, query.refId) }); if (this.angularScope) { this.angularScope.range = getTimeSrv().timeRange(); @@ -144,8 +140,8 @@ export class QueryEditorRow extends PureComponent { }; renderPluginEditor() { - const { query, onChange } = this.props; - const { datasource, queryResponse, queryError } = this.state; + const { query, data, onChange } = this.props; + const { datasource, queryResponse } = this.state; if (datasource.components.QueryCtrl) { return
(this.element = element)} />; @@ -161,7 +157,7 @@ export class QueryEditorRow extends PureComponent { onChange={onChange} onRunQuery={this.onRunQuery} queryResponse={queryResponse} - queryError={queryError} + panelData={data} /> ); } @@ -284,3 +280,33 @@ export interface AngularQueryComponentScope { getCollapsedText?: () => string; range: TimeRange; } + +/** + * Get a version of the PanelData limited to the query we are looking at + */ +export function filterPanelDataToQuery(data: PanelData, refId: string): PanelData | undefined { + const series = data.series.filter(series => series.refId === refId); + + // No matching series + if (!series.length) { + return undefined; + } + + // Don't pass the request if all requests are the same + const request: DataQueryRequest = undefined; + // TODO: look in sub-requets to match the info + + // Only say this is an error if the error links to the query + let state = LoadingState.Done; + const error = data.error && data.error.refId === refId ? data.error : undefined; + if (error) { + state = LoadingState.Error; + } + + return { + state, + series, + request, + error, + }; +}