Browse Source

QueryEditors: pass PanelData and filtered PanelData to each editor (#16692)

Ryan McKinley 6 years ago
parent
commit
b9ff1923e7

+ 3 - 2
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<TQuery extends DataQuery = DataQuery> {
   DataSourceClass: DataSourceConstructor<TQuery>;
@@ -140,8 +141,8 @@ export interface QueryEditorProps<DSType extends DataSourceApi, TQuery extends D
   query: TQuery;
   onRunQuery: () => 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 {

+ 45 - 0
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');
+  });
+});

+ 36 - 10
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<Props, State> {
@@ -46,7 +45,6 @@ export class QueryEditorRow extends PureComponent<Props, State> {
     isCollapsed: false,
     loadedDataSourceValue: undefined,
     hasTextEditMode: false,
-    queryError: null,
     queryResponse: null,
   };
 
@@ -93,9 +91,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
     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<Props, State> {
   };
 
   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 <div ref={element => (this.element = element)} />;
@@ -161,7 +157,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
           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,
+  };
+}