Explorar o código

WIP: good progress on react query editor support

Torkel Ödegaard %!s(int64=7) %!d(string=hai) anos
pai
achega
33feb26fb5

+ 19 - 54
public/app/features/dashboard/panel_editor/QueriesTab.tsx

@@ -3,18 +3,16 @@ import React, { PureComponent } from 'react';
 import _ from 'lodash';
 
 // Components
-import 'app/features/panel/metrics_tab';
 import { EditorTabBody, EditorToolbarView } from './EditorTabBody';
 import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
 import { QueryInspector } from './QueryInspector';
 import { QueryOptions } from './QueryOptions';
-import { AngularQueryComponentScope } from 'app/features/panel/metrics_tab';
 import { PanelOptionsGroup } from '@grafana/ui';
+import { QueryEditorRow } from './QueryEditorRow';
 
 // Services
 import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
 import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv';
-import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader';
 import config from 'app/core/config';
 
 // Types
@@ -37,63 +35,22 @@ interface State {
 }
 
 export class QueriesTab extends PureComponent<Props, State> {
-  element: HTMLElement;
-  component: AngularComponent;
   datasources: DataSourceSelectItem[] = getDatasourceSrv().getMetricSources();
   backendSrv: BackendSrv = getBackendSrv();
 
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      isLoadingHelp: false,
-      currentDS: this.findCurrentDataSource(),
-      helpContent: null,
-      isPickerOpen: false,
-      isAddingMixed: false,
-    };
-  }
+  state: State = {
+    isLoadingHelp: false,
+    currentDS: this.findCurrentDataSource(),
+    helpContent: null,
+    isPickerOpen: false,
+    isAddingMixed: false,
+  };
 
   findCurrentDataSource(): DataSourceSelectItem {
     const { panel } = this.props;
     return this.datasources.find(datasource => datasource.value === panel.datasource) || this.datasources[0];
   }
 
-  getAngularQueryComponentScope(): AngularQueryComponentScope {
-    const { panel, dashboard } = this.props;
-
-    return {
-      panel: panel,
-      dashboard: dashboard,
-      refresh: () => panel.refresh(),
-      render: () => panel.render,
-      addQuery: this.onAddQuery,
-      moveQuery: this.onMoveQuery,
-      removeQuery: this.onRemoveQuery,
-      events: panel.events,
-    };
-  }
-
-  componentDidMount() {
-    if (!this.element) {
-      return;
-    }
-
-    const loader = getAngularLoader();
-    const template = '<metrics-tab />';
-    const scopeProps = {
-      ctrl: this.getAngularQueryComponentScope(),
-    };
-
-    this.component = loader.load(this.element, scopeProps, template);
-  }
-
-  componentWillUnmount() {
-    if (this.component) {
-      this.component.destroy();
-    }
-  }
-
   onChangeDataSource = datasource => {
     const { panel } = this.props;
     const { currentDS } = this.state;
@@ -147,7 +104,6 @@ export class QueriesTab extends PureComponent<Props, State> {
     }
 
     this.props.panel.addQuery();
-    this.component.digest();
     this.forceUpdate();
   };
 
@@ -190,7 +146,6 @@ export class QueriesTab extends PureComponent<Props, State> {
 
   onAddMixedQuery = datasource => {
     this.onAddQuery({ datasource: datasource.name });
-    this.component.digest();
     this.setState({ isAddingMixed: false });
   };
 
@@ -218,7 +173,17 @@ export class QueriesTab extends PureComponent<Props, State> {
         <>
           <PanelOptionsGroup>
             <div className="query-editor-rows">
-              <div ref={element => (this.element = element)} />
+              {panel.targets.map((query, index) => (
+                <QueryEditorRow
+                  datasourceName={query.datasource || panel.datasource}
+                  key={query.refId}
+                  panel={panel}
+                  query={query}
+                  onRemoveQuery={this.onRemoveQuery}
+                  onAddQuery={this.onAddQuery}
+                  onMoveQuery={this.onMoveQuery}
+                />
+              ))}
 
               <div className="gf-form-query">
                 <div className="gf-form gf-form-query-letter-cell">

+ 99 - 7
public/app/features/dashboard/panel_editor/QueryEditorRow.tsx

@@ -2,29 +2,121 @@
 import React, { PureComponent } from 'react';
 
 // Utils & Services
+import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
 import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader';
+import { Emitter } from 'app/core/utils/emitter';
 
 // Types
 import { PanelModel } from '../panel_model';
-import { DashboardModel } from '../dashboard_model';
+import { DataQuery, DataSourceApi } from 'app/types/series';
 
 interface Props {
   panel: PanelModel;
-  dashboard: DashboardModel;
+  query: DataQuery;
+  onAddQuery: (query?: DataQuery) => void;
+  onRemoveQuery: (query: DataQuery) => void;
+  onMoveQuery: (query: DataQuery, direction: number) => void;
+  datasourceName: string | null;
 }
 
 interface State {
+  datasource: DataSourceApi | null;
 }
 
-export class VisualizationTab extends PureComponent<Props, State> {
-  element: HTMLElement;
-  angularQueryEditor: AngularComponent;
+export class QueryEditorRow extends PureComponent<Props, State> {
+  element: HTMLElement | null = null;
+  angularQueryEditor: AngularComponent | null = null;
 
-  constructor(props) {
-    super(props);
+  state: State = {
+    datasource: null,
+  };
+
+  componentDidMount() {
+    this.loadDatasource();
+  }
+
+  getAngularQueryComponentScope(): AngularQueryComponentScope {
+    const { panel, onAddQuery, onMoveQuery, onRemoveQuery, query } = this.props;
+    const { datasource } = this.state;
+
+    return {
+      datasource: datasource,
+      target: query,
+      panel: panel,
+      refresh: () => panel.refresh(),
+      render: () => panel.render,
+      addQuery: onAddQuery,
+      moveQuery: onMoveQuery,
+      removeQuery: onRemoveQuery,
+      events: panel.events,
+    };
+  }
+
+  async loadDatasource() {
+    const { query, panel } = this.props;
+    const dataSourceSrv = getDatasourceSrv();
+    const datasource = await dataSourceSrv.get(query.datasource || panel.datasource);
+
+    this.setState({ datasource });
+  }
+
+  componentDidUpdate() {
+    const { datasource } = this.state;
+
+    // check if we need to load another datasource
+    if (datasource && datasource.name !== this.props.datasourceName) {
+      if (this.angularQueryEditor) {
+        this.angularQueryEditor.destroy();
+        this.angularQueryEditor = null;
+      }
+      this.loadDatasource();
+      return;
+    }
+
+    if (!this.element || this.angularQueryEditor) {
+      return;
+    }
+
+    const loader = getAngularLoader();
+    const template = '<plugin-component type="query-ctrl" />';
+    const scopeProps = { ctrl: this.getAngularQueryComponentScope() };
+
+    this.angularQueryEditor = loader.load(this.element, scopeProps, template);
+  }
+
+  componentWillUnmount() {
+    if (this.angularQueryEditor) {
+      this.angularQueryEditor.destroy();
+    }
   }
 
   render() {
+    const { datasource } = this.state;
+
+    if (!datasource) {
+      return null;
+    }
 
+    if (datasource.pluginExports.QueryCtrl) {
+      return <div ref={element => (this.element = element)} />;
+    } else if (datasource.pluginExports.QueryEditor) {
+      const QueryEditor = datasource.pluginExports.QueryEditor;
+      return <QueryEditor />;
+    }
+
+    return <div>Data source plugin does not export any Query Editor component</div>;
   }
 }
+
+export interface AngularQueryComponentScope {
+  target: DataQuery;
+  panel: PanelModel;
+  events: Emitter;
+  refresh: () => void;
+  render: () => void;
+  removeQuery: (query: DataQuery) => void;
+  addQuery: (query?: DataQuery) => void;
+  moveQuery: (query: DataQuery, direction: number) => void;
+  datasource: DataSourceApi;
+}
+

+ 0 - 31
public/app/features/panel/metrics_tab.ts

@@ -1,31 +0,0 @@
-// Services & utils
-import coreModule from 'app/core/core_module';
-import { Emitter } from 'app/core/utils/emitter';
-
-// Types
-import { DashboardModel } from '../dashboard/dashboard_model';
-import { PanelModel } from '../dashboard/panel_model';
-import { DataQuery } from 'app/types';
-
-export interface AngularQueryComponentScope {
-  panel: PanelModel;
-  dashboard: DashboardModel;
-  events: Emitter;
-  refresh: () => void;
-  render: () => void;
-  removeQuery: (query: DataQuery) => void;
-  addQuery: (query?: DataQuery) => void;
-  moveQuery: (query: DataQuery, direction: number) => void;
-}
-
-/** @ngInject */
-export function metricsTabDirective() {
-  'use strict';
-  return {
-    restrict: 'E',
-    scope: true,
-    templateUrl: 'public/app/features/panel/partials/metrics_tab.html',
-  };
-}
-
-coreModule.directive('metricsTab', metricsTabDirective);

+ 0 - 24
public/app/features/panel/partials/metrics_tab.html

@@ -1,24 +0,0 @@
-	<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">
-		<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
-			<plugin-component type="query-ctrl">
-			</plugin-component>
-		</rebuild-on-change>
-	</div>
-
-	<!-- <div class="gf&#45;form&#45;query"> -->
-	<!-- 	<div class="gf&#45;form gf&#45;form&#45;query&#45;letter&#45;cell"> -->
-	<!-- 		<label class="gf&#45;form&#45;label"> -->
-	<!-- 			<span class="gf&#45;form&#45;query&#45;letter&#45;cell&#45;carret"> -->
-	<!-- 				<i class="fa fa&#45;caret&#45;down"></i> -->
-	<!-- 			</span> -->
-	<!-- 			<span class="gf&#45;form&#45;query&#45;letter&#45;cell&#45;letter">{{ctrl.nextRefId}}</span> -->
-	<!-- 		</label> -->
-	<!-- 		<button class="btn btn&#45;secondary gf&#45;form&#45;btn" ng&#45;click="ctrl.addQuery()" ng&#45;hide="ctrl.datasourceInstance.meta.mixed"> -->
-	<!-- 			Add Query -->
-	<!-- 		</button> -->
-	<!-- 		<div class="dropdown" ng&#45;if="ctrl.datasourceInstance.meta.mixed"> -->
-	<!-- 			<gf&#45;form&#45;dropdown model="ctrl.addQueryDropdown" get&#45;options="ctrl.getOptions(false)" on&#45;change="ctrl.addMixedQuery($option)"> -->
-	<!-- 			</gf&#45;form&#45;dropdown> -->
-	<!-- 		</div> -->
-	<!-- 	</div> -->
-	<!-- </div> -->

+ 11 - 17
public/app/features/plugins/plugin_component.ts

@@ -105,23 +105,17 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
     switch (attrs.type) {
       // QueryCtrl
       case 'query-ctrl': {
-        const datasource = scope.target.datasource || scope.ctrl.panel.datasource;
-        return datasourceSrv.get(datasource).then(ds => {
-          scope.datasource = ds;
-
-          return importPluginModule(ds.meta.module).then(dsModule => {
-            return {
-              baseUrl: ds.meta.baseUrl,
-              name: 'query-ctrl-' + ds.meta.id,
-              bindings: { target: '=', panelCtrl: '=', datasource: '=' },
-              attrs: {
-                target: 'target',
-                'panel-ctrl': 'ctrl',
-                datasource: 'datasource',
-              },
-              Component: dsModule.QueryCtrl,
-            };
-          });
+        const ds = scope.ctrl.datasource;
+        return $q.when({
+          baseUrl: ds.meta.baseUrl,
+          name: 'query-ctrl-' + ds.meta.id,
+          bindings: { target: '=', panelCtrl: '=', datasource: '=' },
+          attrs: {
+            target: 'ctrl.target',
+            'panel-ctrl': 'ctrl',
+            datasource: 'ctrl.datasource',
+          },
+          Component: ds.pluginExports.QueryCtrl,
         });
       }
       // Annotations

+ 1 - 0
public/app/types/plugins.ts

@@ -4,6 +4,7 @@ import { PanelProps, PanelOptionsProps } from '@grafana/ui';
 export interface PluginExports {
   Datasource?: any;
   QueryCtrl?: any;
+  QueryEditor?: any;
   ConfigCtrl?: any;
   AnnotationsQueryCtrl?: any;
   VariableQueryEditor?: any;

+ 5 - 1
public/app/types/series.ts

@@ -1,4 +1,4 @@
-import { PluginMeta } from './plugins';
+import { PluginMeta, PluginExports } from './plugins';
 import { TimeSeries, TimeRange, RawTimeRange } from '@grafana/ui';
 
 export interface DataQueryResponse {
@@ -25,6 +25,10 @@ export interface DataQueryOptions {
 }
 
 export interface DataSourceApi {
+  name: string;
+  meta: PluginMeta;
+  pluginExports: PluginExports;
+
   /**
    *  min interval range
    */