Selaa lähdekoodia

Merge branch 'panel-edit-in-react' of github.com:grafana/grafana into panel-edit-in-react

ijin08 7 vuotta sitten
vanhempi
commit
46ec9bd6ad

+ 1 - 0
pkg/plugins/datasource_plugin.go

@@ -24,6 +24,7 @@ type DataSourcePlugin struct {
 	Metrics      bool              `json:"metrics"`
 	Alerting     bool              `json:"alerting"`
 	Explore      bool              `json:"explore"`
+	Table        bool              `json:"tables"`
 	Logs         bool              `json:"logs"`
 	QueryOptions map[string]bool   `json:"queryOptions,omitempty"`
 	BuiltIn      bool              `json:"builtIn,omitempty"`

+ 2 - 3
public/app/core/table_model.ts

@@ -86,11 +86,10 @@ export function mergeTablesIntoModel(dst?: TableModel, ...tables: TableModel[]):
   if (arguments.length === 1) {
     return model;
   }
-
   // Single query returns data columns and rows as is
   if (arguments.length === 2) {
-    model.columns = [...tables[0].columns];
-    model.rows = [...tables[0].rows];
+    model.columns = tables[0].hasOwnProperty('columns') ? [...tables[0].columns] : [];
+    model.rows = tables[0].hasOwnProperty('rows') ? [...tables[0].rows] : [];
     return model;
   }
 

+ 35 - 12
public/app/features/explore/Explore.tsx

@@ -12,7 +12,7 @@ import {
   QueryHintGetter,
   QueryHint,
 } from 'app/types/explore';
-import { RawTimeRange, DataQuery } from 'app/types/series';
+import { TimeRange, DataQuery } from 'app/types/series';
 import store from 'app/core/store';
 import {
   DEFAULT_RANGE,
@@ -30,6 +30,8 @@ import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer'
 import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
 import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
 import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
+import { Emitter } from 'app/core/utils/emitter';
+import * as dateMath from 'app/core/utils/datemath';
 
 import Panel from './Panel';
 import QueryRows from './QueryRows';
@@ -88,6 +90,7 @@ interface ExploreProps {
  */
 export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
   el: any;
+  exploreEvents: Emitter;
   /**
    * Current query expressions of the rows including their modifications, used for running queries.
    * Not kept in component state to prevent edit-render roundtrips.
@@ -132,6 +135,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
       };
     }
     this.modifiedQueries = initialQueries.slice();
+    this.exploreEvents = new Emitter();
   }
 
   async componentDidMount() {
@@ -155,19 +159,20 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
       } else {
         datasource = await datasourceSrv.get();
       }
-      if (!datasource.meta.explore) {
-        datasource = await datasourceSrv.get(datasources[0].name);
-      }
       await this.setDatasource(datasource);
     } else {
       this.setState({ datasourceMissing: true });
     }
   }
 
+  componentWillUnmount() {
+    this.exploreEvents.removeAllListeners();
+  }
+
   async setDatasource(datasource: any, origin?: DataSource) {
     const supportsGraph = datasource.meta.metrics;
     const supportsLogs = datasource.meta.logs;
-    const supportsTable = datasource.meta.metrics;
+    const supportsTable = datasource.meta.tables;
     const datasourceId = datasource.meta.id;
     let datasourceError = null;
 
@@ -317,8 +322,14 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
     }
   };
 
-  onChangeTime = (nextRange: RawTimeRange) => {
-    const range: RawTimeRange = {
+  // onChangeTime = (nextRange: RawTimeRange) => {
+  //   const range: RawTimeRange = {
+  //     ...nextRange,
+  //   };
+  //   this.setState({ range }, () => this.onSubmit());
+  // };
+  onChangeTime = (nextRange: TimeRange) => {
+    const range: TimeRange = {
       ...nextRange,
     };
     this.setState({ range }, () => this.onSubmit());
@@ -538,8 +549,8 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
     ];
 
     // Clone range for query request
-    const queryRange: RawTimeRange = { ...range };
-
+    // const queryRange: RawTimeRange = { ...range };
+    // const { from, to, raw } = this.timeSrv.timeRange();
     // Datasource is using `panelId + query.refId` for cancellation logic.
     // Using `format` here because it relates to the view panel that the request is for.
     const panelId = queryOptions.format;
@@ -549,7 +560,12 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
       intervalMs,
       panelId,
       targets: configuredQueries, // Datasources rely on DataQueries being passed under the targets key.
-      range: queryRange,
+      range: {
+        from: dateMath.parse(range.from, false),
+        to: dateMath.parse(range.to, true),
+        raw: range,
+      },
+      rangeRaw: range,
     };
   }
 
@@ -696,17 +712,19 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
     }
     const { datasource } = this.state;
     const datasourceId = datasource.meta.id;
-    // Run all queries concurrently
+    // Run all queries concurrentlyso
     queries.forEach(async (query, rowIndex) => {
       const transaction = this.startQueryTransaction(query, rowIndex, resultType, queryOptions);
       try {
         const now = Date.now();
         const res = await datasource.query(transaction.options);
+        this.exploreEvents.emit('data-received', res);
         const latency = Date.now() - now;
         const results = resultGetter ? resultGetter(res.data) : res.data;
         this.completeQueryTransaction(transaction.id, results, latency, queries, datasourceId);
         this.setState({ graphRange: transaction.options.range });
       } catch (response) {
+        this.exploreEvents.emit('data-error', response);
         this.failQueryTransaction(transaction.id, response, datasourceId);
       }
     });
@@ -759,7 +777,10 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
     const graphResult = _.flatten(
       queryTransactions.filter(qt => qt.resultType === 'Graph' && qt.done && qt.result).map(qt => qt.result)
     );
-    const tableResult = mergeTablesIntoModel(
+
+    //Temp solution... How do detect if ds supports table format?
+    let tableResult;
+    tableResult = mergeTablesIntoModel(
       new TableModel(),
       ...queryTransactions.filter(qt => qt.resultType === 'Table' && qt.done && qt.result).map(qt => qt.result)
     );
@@ -858,6 +879,8 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
               onExecuteQuery={this.onSubmit}
               onRemoveQueryRow={this.onRemoveQueryRow}
               transactions={queryTransactions}
+              exploreEvents={this.exploreEvents}
+              range={range}
             />
             <main className="m-t-2">
               <ErrorBoundary>

+ 77 - 0
public/app/features/explore/QueryEditor.tsx

@@ -0,0 +1,77 @@
+import React, { PureComponent } from 'react';
+import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
+import { Emitter } from 'app/core/utils/emitter';
+import { getIntervals } from 'app/core/utils/explore';
+import { DataQuery } from 'app/types';
+import { RawTimeRange } from 'app/types/series';
+import { getTimeSrv } from 'app/features/dashboard/time_srv';
+import 'app/features/plugins/plugin_loader';
+
+interface QueryEditorProps {
+  datasource: any;
+  error?: string | JSX.Element;
+  onExecuteQuery?: () => void;
+  onQueryChange?: (value: DataQuery, override?: boolean) => void;
+  initialQuery: DataQuery;
+  exploreEvents: Emitter;
+  range: RawTimeRange;
+}
+
+export default class QueryEditor extends PureComponent<QueryEditorProps, any> {
+  element: any;
+  component: AngularComponent;
+
+  async componentDidMount() {
+    if (!this.element) {
+      return;
+    }
+
+    const { datasource, initialQuery, exploreEvents, range } = this.props;
+    this.initTimeSrv(range);
+
+    const loader = getAngularLoader();
+    const template = '<plugin-component type="query-ctrl"> </plugin-component>';
+    const target = { datasource: datasource.name, ...initialQuery };
+    const scopeProps = {
+      target,
+      ctrl: {
+        refresh: () => {
+          this.props.onQueryChange({ refId: initialQuery.refId, ...target }, false);
+          this.props.onExecuteQuery();
+        },
+        events: exploreEvents,
+        panel: {
+          datasource,
+          targets: [target],
+        },
+        dashboard: {
+          getNextQueryLetter: x => '',
+        },
+        hideEditorRowActions: true,
+        ...getIntervals(range, datasource, null), // Possible to get resolution?
+      },
+    };
+
+    this.component = loader.load(this.element, scopeProps, template);
+  }
+
+  componentWillUnmount() {
+    if (this.component) {
+      this.component.destroy();
+    }
+  }
+
+  initTimeSrv(range) {
+    const timeSrv = getTimeSrv();
+    timeSrv.init({
+      time: range,
+      refresh: false,
+      getTimezone: () => 'utc',
+      timeRangeUpdated: () => console.log('refreshDashboard!'),
+    });
+  }
+
+  render() {
+    return <div ref={element => (this.element = element)} style={{ width: '100%' }} />;
+  }
+}

+ 37 - 13
public/app/features/explore/QueryRows.tsx

@@ -1,10 +1,13 @@
 import React, { PureComponent } from 'react';
 
 import { QueryTransaction, HistoryItem, QueryHint } from 'app/types/explore';
+import { Emitter } from 'app/core/utils/emitter';
 
-import DefaultQueryField from './QueryField';
+// import DefaultQueryField from './QueryField';
+import QueryEditor from './QueryEditor';
 import QueryTransactionStatus from './QueryTransactionStatus';
 import { DataSource, DataQuery } from 'app/types';
+import { RawTimeRange } from 'app/types/series';
 
 function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint {
   const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0);
@@ -27,6 +30,8 @@ interface QueryRowCommonProps {
   datasource: DataSource;
   history: HistoryItem[];
   transactions: QueryTransaction[];
+  exploreEvents: Emitter;
+  range: RawTimeRange;
 }
 
 type QueryRowProps = QueryRowCommonProps &
@@ -36,6 +41,11 @@ type QueryRowProps = QueryRowCommonProps &
   };
 
 class QueryRow extends PureComponent<QueryRowProps> {
+  onExecuteQuery = () => {
+    const { onExecuteQuery } = this.props;
+    onExecuteQuery();
+  };
+
   onChangeQuery = (value: DataQuery, override?: boolean) => {
     const { index, onChangeQuery } = this.props;
     if (onChangeQuery) {
@@ -76,27 +86,41 @@ class QueryRow extends PureComponent<QueryRowProps> {
   };
 
   render() {
-    const { datasource, history, initialQuery, transactions } = this.props;
+    const { datasource, history, initialQuery, transactions, exploreEvents, range } = this.props;
     const transactionWithError = transactions.find(t => t.error !== undefined);
     const hint = getFirstHintFromTransactions(transactions);
     const queryError = transactionWithError ? transactionWithError.error : null;
-    const QueryField = datasource.pluginExports.ExploreQueryField || DefaultQueryField;
+    // const QueryField = datasource.pluginExports.ExploreQueryField || DefaultQueryField;
+    const QueryField = datasource.pluginExports.ExploreQueryField;
+    // const QueryEditor = datasource.pluginExports.QueryCtrl;
     return (
       <div className="query-row">
         <div className="query-row-status">
           <QueryTransactionStatus transactions={transactions} />
         </div>
         <div className="query-row-field">
-          <QueryField
-            datasource={datasource}
-            error={queryError}
-            hint={hint}
-            initialQuery={initialQuery}
-            history={history}
-            onClickHintFix={this.onClickHintFix}
-            onPressEnter={this.onPressEnter}
-            onQueryChange={this.onChangeQuery}
-          />
+          {QueryField ? (
+            <QueryField
+              datasource={datasource}
+              error={queryError}
+              hint={hint}
+              initialQuery={initialQuery}
+              history={history}
+              onClickHintFix={this.onClickHintFix}
+              onPressEnter={this.onPressEnter}
+              onQueryChange={this.onChangeQuery}
+            />
+          ) : (
+            <QueryEditor
+              datasource={datasource}
+              error={queryError}
+              onQueryChange={this.onChangeQuery}
+              onExecuteQuery={this.onExecuteQuery}
+              initialQuery={initialQuery}
+              exploreEvents={exploreEvents}
+              range={range}
+            />
+          )}
         </div>
         <div className="query-row-tools">
           <button className="btn navbar-button navbar-button--tight" onClick={this.onClickClearButton}>

+ 8 - 2
public/app/features/explore/TimePicker.tsx

@@ -3,7 +3,7 @@ import moment from 'moment';
 
 import * as dateMath from 'app/core/utils/datemath';
 import * as rangeUtil from 'app/core/utils/rangeutil';
-import { RawTimeRange } from 'app/types/series';
+import { RawTimeRange, TimeRange } from 'app/types/series';
 
 const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss';
 export const DEFAULT_RANGE = {
@@ -120,6 +120,12 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
       to: moment(nextTo),
     };
 
+    const nextTimeRange: TimeRange = {
+      raw: nextRange,
+      from,
+      to,
+    };
+
     this.setState(
       {
         rangeString: rangeUtil.describeTimeRange(nextRange),
@@ -127,7 +133,7 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
         toRaw: nextRange.to.format(DATE_FORMAT),
       },
       () => {
-        onChangeTime(nextRange);
+        onChangeTime(nextTimeRange);
       }
     );
   }

+ 31 - 46
public/app/features/panel/partials/query_editor_row.html

@@ -1,59 +1,44 @@
-
 <div class="gf-form-query">
-	<div class="gf-form gf-form-query-letter-cell">
+  <div ng-if="!ctrl.hideEditorRowActions" class="gf-form gf-form-query-letter-cell">
     <label class="gf-form-label">
       <a class="pointer" tabindex="1" ng-click="ctrl.toggleCollapse()">
-        <span  ng-class="{muted: !ctrl.canCollapse}" class="gf-form-query-letter-cell-carret">
+        <span ng-class="{muted: !ctrl.canCollapse}" class="gf-form-query-letter-cell-carret">
           <i class="fa fa-caret-down" ng-hide="ctrl.collapsed"></i>
           <i class="fa fa-caret-right" ng-show="ctrl.collapsed"></i>
         </span>
-        <span class="gf-form-query-letter-cell-letter">{{ctrl.target.refId}}</span>
-        <em class="gf-form-query-letter-cell-ds" ng-show="ctrl.target.datasource">({{ctrl.target.datasource}})</em>
+        <span class="gf-form-query-letter-cell-letter">{{ ctrl.target.refId }}</span>
+        <em class="gf-form-query-letter-cell-ds" ng-show="ctrl.target.datasource">({{ ctrl.target.datasource }})</em>
       </a>
-		</label>
+    </label>
   </div>
 
-	<div class="gf-form-query-content gf-form-query-content--collapsed" ng-if="ctrl.collapsed">
-		<div class="gf-form">
-			<label class="gf-form-label pointer gf-form-label--grow" ng-click="ctrl.toggleCollapse()">
-				{{ctrl.collapsedText}}
-			</label>
-		</div>
-	</div>
+  <div class="gf-form-query-content gf-form-query-content--collapsed" ng-if="ctrl.collapsed">
+    <div class="gf-form">
+      <label class="gf-form-label pointer gf-form-label--grow" ng-click="ctrl.toggleCollapse()">
+        {{ ctrl.collapsedText }}
+      </label>
+    </div>
+  </div>
 
-	<div ng-transclude class="gf-form-query-content" ng-if="!ctrl.collapsed">
-	</div>
+  <div ng-transclude class="gf-form-query-content" ng-if="!ctrl.collapsed"></div>
 
-	<div class="gf-form">
-		<label class="gf-form-label dropdown">
-			<a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1">
-				<i class="fa fa-bars"></i>
-			</a>
-			<ul class="dropdown-menu pull-right" role="menu">
-				<li role="menuitem" ng-if="ctrl.hasTextEditMode">
-					<a tabindex="1" ng-click="ctrl.toggleEditorMode()">Toggle Edit Mode</a>
-				</li>
-				<li role="menuitem">
-					<a tabindex="1" ng-click="ctrl.duplicateQuery()">Duplicate</a>
-				</li>
-				<li role="menuitem">
-					<a tabindex="1" ng-click="ctrl.moveQuery(-1)">Move up</a>
-				</li>
-				<li role="menuitem">
-					<a tabindex="1" ng-click="ctrl.moveQuery(1)">Move down</a>
-				</li>
-			</ul>
-		</label>
+  <div ng-if="!ctrl.hideEditorRowActions" class="gf-form">
+    <label class="gf-form-label dropdown">
+      <a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1"> <i class="fa fa-bars"></i> </a>
+      <ul class="dropdown-menu pull-right" role="menu">
+        <li role="menuitem" ng-if="ctrl.hasTextEditMode">
+          <a tabindex="1" ng-click="ctrl.toggleEditorMode()">Toggle Edit Mode</a>
+        </li>
+        <li role="menuitem"><a tabindex="1" ng-click="ctrl.duplicateQuery()">Duplicate</a></li>
+        <li role="menuitem"><a tabindex="1" ng-click="ctrl.moveQuery(-1)">Move up</a></li>
+        <li role="menuitem"><a tabindex="1" ng-click="ctrl.moveQuery(1)">Move down</a></li>
+      </ul>
+    </label>
+    <label class="gf-form-label">
+      <a ng-click="ctrl.toggleHideQuery()" role="menuitem"> <i class="fa fa-eye"></i> </a>
+    </label>
     <label class="gf-form-label">
-			<a ng-click="ctrl.toggleHideQuery()" role="menuitem">
-				<i class="fa fa-eye"></i>
-			</a>
-		</label>
-		<label class="gf-form-label">
-			<a class="pointer" tabindex="1" ng-click="ctrl.removeQuery(ctrl.target)">
-				<i class="fa fa-trash"></i>
-			</a>
-		</label>
-	</div>
+      <a class="pointer" tabindex="1" ng-click="ctrl.removeQuery(ctrl.target)"> <i class="fa fa-trash"></i> </a>
+    </label>
+  </div>
 </div>
-

+ 2 - 0
public/app/features/panel/query_editor_row.ts

@@ -11,11 +11,13 @@ export class QueryRowCtrl {
   panelCtrl: any;
   panel: any;
   collapsed: any;
+  hideEditorRowActions: boolean;
 
   constructor() {
     this.panelCtrl = this.queryCtrl.panelCtrl;
     this.target = this.queryCtrl.target;
     this.panel = this.panelCtrl.panel;
+    this.hideEditorRowActions = this.panelCtrl.hideEditorRowActions;
 
     if (!this.target.refId) {
       this.target.refId = this.panelCtrl.dashboard.getNextQueryLetter(this.panel);

+ 1 - 3
public/app/features/plugins/datasource_srv.ts

@@ -93,9 +93,7 @@ export class DatasourceSrv {
 
   getExploreSources() {
     const { datasources } = config;
-    const es = Object.keys(datasources)
-      .map(name => datasources[name])
-      .filter(ds => ds.meta && ds.meta.explore);
+    const es = Object.keys(datasources).map(name => datasources[name]);
     return _.sortBy(es, ['name']);
   }
 

+ 7 - 5
public/app/plugins/datasource/graphite/plugin.json

@@ -3,13 +3,12 @@
   "type": "datasource",
   "id": "graphite",
 
-  "includes": [
-    {"type": "dashboard", "name": "Graphite Carbon Metrics", "path": "dashboards/carbon_metrics.json"}
-  ],
+  "includes": [{ "type": "dashboard", "name": "Graphite Carbon Metrics", "path": "dashboards/carbon_metrics.json" }],
 
   "metrics": true,
   "alerting": true,
   "annotations": true,
+  "tables": false,
 
   "queryOptions": {
     "maxDataPoints": true,
@@ -27,8 +26,11 @@
       "large": "img/graphite_logo.png"
     },
     "links": [
-      {"name": "Graphite", "url": "https://graphiteapp.org/"},
-      {"name": "Graphite 1.1 Release", "url": "https://grafana.com/blog/2018/01/11/graphite-1.1-teaching-an-old-dog-new-tricks/"}
+      { "name": "Graphite", "url": "https://graphiteapp.org/" },
+      {
+        "name": "Graphite 1.1 Release",
+        "url": "https://grafana.com/blog/2018/01/11/graphite-1.1-teaching-an-old-dog-new-tricks/"
+      }
     ],
     "version": "5.0.0"
   }

+ 2 - 1
public/app/plugins/datasource/logging/plugin.json

@@ -7,6 +7,7 @@
   "annotations": false,
   "logs": true,
   "explore": true,
+  "tables": true,
   "info": {
     "description": "Grafana Logging Data Source for Grafana",
     "author": {
@@ -25,4 +26,4 @@
     ],
     "version": "5.3.0"
   }
-}
+}

+ 1 - 1
public/app/plugins/datasource/mssql/plugin.json

@@ -18,9 +18,9 @@
   "alerting": true,
   "annotations": true,
   "metrics": true,
+  "tables": true,
 
   "queryOptions": {
     "minInterval": true
   }
-
 }

+ 1 - 1
public/app/plugins/datasource/mysql/plugin.json

@@ -19,9 +19,9 @@
   "alerting": true,
   "annotations": true,
   "metrics": true,
+  "tables": true,
 
   "queryOptions": {
     "minInterval": true
   }
-
 }

+ 1 - 0
public/app/plugins/datasource/opentsdb/plugin.json

@@ -7,6 +7,7 @@
   "defaultMatchFormat": "pipe",
   "annotations": true,
   "alerting": true,
+  "tables": false,
 
   "info": {
     "description": "OpenTSDB Data Source for Grafana",

+ 1 - 1
public/app/plugins/datasource/postgres/plugin.json

@@ -19,9 +19,9 @@
   "alerting": true,
   "annotations": true,
   "metrics": true,
+  "tables": true,
 
   "queryOptions": {
     "minInterval": true
   }
-
 }

+ 2 - 1
public/app/plugins/datasource/prometheus/plugin.json

@@ -23,6 +23,7 @@
   "alerting": true,
   "annotations": true,
   "explore": true,
+  "tables": true,
   "queryOptions": {
     "minInterval": true
   },
@@ -44,4 +45,4 @@
     ],
     "version": "5.0.0"
   }
-}
+}

+ 1 - 0
public/app/plugins/datasource/stackdriver/plugin.json

@@ -5,6 +5,7 @@
   "metrics": true,
   "alerting": true,
   "annotations": true,
+  "tables": false,
   "state": "beta",
   "queryOptions": {
     "maxDataPoints": true,

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

@@ -41,6 +41,7 @@ export interface PluginMeta {
 
   // Datasource-specific
   metrics?: boolean;
+  tables?: boolean;
   logs?: boolean;
   explore?: boolean;
   annotations?: boolean;

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 187
yarn.lock


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä