From 3c733638ee19152e171a5748b2d841f5a1c9e340 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Thu, 9 May 2019 22:09:48 -0700 Subject: [PATCH] MetricsPanelCtrl: use shared queryRunner to support query execution (#16659) * move queryRunner to panelModel * remove isEditing from PanelChrome * move listener to QueriesTab * use queryRunner on MetricsPanelCtrl * Added interval back as prop, not used yet * QueryRunner: worked on tests * PanelQueryRunner: Refactoring, added getQueryRunner to PanelModel * PanelQueryRunner: interpolatel min interval * merge master and remove grafana-live --- .../app/features/panel/metrics_panel_ctrl.ts | 209 +++++++++--------- .../datasource/grafana-live/_plugin.json | 7 - .../datasource/grafana-live/datasource.ts | 34 --- .../plugins/datasource/grafana-live/module.ts | 8 - .../grafana-live/partials/query.editor.html | 8 - 5 files changed, 101 insertions(+), 165 deletions(-) delete mode 100644 public/app/plugins/datasource/grafana-live/_plugin.json delete mode 100644 public/app/plugins/datasource/grafana-live/datasource.ts delete mode 100644 public/app/plugins/datasource/grafana-live/module.ts delete mode 100644 public/app/plugins/datasource/grafana-live/partials/query.editor.html diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index b8ee1cff372..068aa03d0f9 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -6,12 +6,23 @@ import { PanelCtrl } from 'app/features/panel/panel_ctrl'; import { getExploreUrl } from 'app/core/utils/explore'; import { applyPanelTimeOverrides, getResolution } from 'app/features/dashboard/utils/panel'; import { ContextSrv } from 'app/core/services/context_srv'; -import { toLegacyResponseData, isSeriesData, LegacyResponseData, TimeRange } from '@grafana/ui'; +import { + toLegacyResponseData, + isSeriesData, + LegacyResponseData, + TimeRange, + DataSourceApi, + PanelData, + LoadingState, + DataQueryResponse, +} from '@grafana/ui'; import { Unsubscribable } from 'rxjs'; +import { PanelModel } from 'app/features/dashboard/state'; +import { PanelQueryRunnerFormat } from '../dashboard/state/PanelQueryRunner'; class MetricsPanelCtrl extends PanelCtrl { scope: any; - datasource: any; + datasource: DataSourceApi; $q: any; $timeout: any; contextSrv: ContextSrv; @@ -24,11 +35,10 @@ class MetricsPanelCtrl extends PanelCtrl { resolution: any; timeInfo?: string; skipDataOnInit: boolean; - dataStream: any; - dataSubscription?: Unsubscribable; dataList: LegacyResponseData[]; + querySubscription?: Unsubscribable; - constructor($scope, $injector) { + constructor($scope: any, $injector: any) { super($scope, $injector); this.$q = $injector.get('$q'); @@ -44,9 +54,9 @@ class MetricsPanelCtrl extends PanelCtrl { } private onPanelTearDown() { - if (this.dataSubscription) { - this.dataSubscription.unsubscribe(); - this.dataSubscription = null; + if (this.querySubscription) { + this.querySubscription.unsubscribe(); + this.querySubscription = null; } } @@ -72,47 +82,76 @@ class MetricsPanelCtrl extends PanelCtrl { }); } - // // ignore if we have data stream - if (this.dataStream) { - return; - } - // clear loading/error state delete this.error; this.loading = true; // load datasource service - this.datasourceSrv - .get(this.panel.datasource, this.panel.scopedVars) - .then(this.updateTimeRange.bind(this)) - .then(this.issueQueries.bind(this)) - .then(this.handleQueryResult.bind(this)) - .catch(err => { - // if canceled keep loading set to true - if (err.cancelled) { - console.log('Panel request cancelled', err); - return; - } - - this.loading = false; - this.error = err.message || 'Request Error'; - this.inspector = { error: err }; - - if (err.data) { - if (err.data.message) { - this.error = err.data.message; - } - if (err.data.error) { - this.error = err.data.error; - } - } - - this.events.emit('data-error', err); - console.log('Panel data error:', err); - }); + return ( + this.datasourceSrv + .get(this.panel.datasource, this.panel.scopedVars) + .then(this.updateTimeRange.bind(this)) + .then(this.issueQueries.bind(this)) + // NOTE handleQueryResult is called by panelDataObserver + .catch((err: any) => { + this.processDataError(err); + }) + ); } - updateTimeRange(datasource?) { + processDataError(err: any) { + // if canceled keep loading set to true + if (err.cancelled) { + console.log('Panel request cancelled', err); + return; + } + + this.loading = false; + this.error = err.message || 'Request Error'; + this.inspector = { error: err }; + + if (err.data) { + if (err.data.message) { + this.error = err.data.message; + } + if (err.data.error) { + this.error = err.data.error; + } + } + + this.events.emit('data-error', err); + console.log('Panel data error:', err); + } + + // Updates the response with information from the stream + panelDataObserver = { + next: (data: PanelData) => { + if (data.state === LoadingState.Error) { + this.loading = false; + this.processDataError(data.error); + } else if (data.state === LoadingState.Done) { + this.loading = false; + + // The result should already be processed, but just in case + if (!data.legacy) { + data.legacy = data.series.map(v => { + if (isSeriesData(v)) { + return toLegacyResponseData(v); + } + return v; + }); + } + + // Make the results look like they came directly from a <6.2 datasource request + // NOTE: any object other than 'data' is no longer supported supported + this.handleQueryResult({ + data: data.legacy, + }); + } + }, + }; + + updateTimeRange(datasource?: DataSourceApi) { this.datasource = datasource || this.datasource; this.range = this.timeSrv.timeRange(); this.resolution = getResolution(this.panel); @@ -141,46 +180,34 @@ class MetricsPanelCtrl extends PanelCtrl { this.intervalMs = res.intervalMs; } - issueQueries(datasource) { + issueQueries(datasource: DataSourceApi) { this.datasource = datasource; - if (!this.panel.targets || this.panel.targets.length === 0) { - return this.$q.when([]); + const panel = this.panel as PanelModel; + const queryRunner = panel.getQueryRunner(); + + if (!this.querySubscription) { + this.querySubscription = queryRunner.subscribe(this.panelDataObserver, PanelQueryRunnerFormat.legacy); } - // make shallow copy of scoped vars, - // and add built in variables interval and interval_ms - const scopedVars = Object.assign({}, this.panel.scopedVars, { - __interval: { text: this.interval, value: this.interval }, - __interval_ms: { text: this.intervalMs, value: this.intervalMs }, - }); - - const metricsQuery = { - timezone: this.dashboard.getTimezone(), - panelId: this.panel.id, + return queryRunner.run({ + datasource: panel.datasource, + queries: panel.targets, + panelId: panel.id, dashboardId: this.dashboard.id, - range: this.range, - rangeRaw: this.range.raw, - interval: this.interval, - intervalMs: this.intervalMs, - targets: this.panel.targets, - maxDataPoints: this.resolution, - scopedVars: scopedVars, - cacheTimeout: this.panel.cacheTimeout, - }; - - return datasource.query(metricsQuery); + timezone: this.dashboard.timezone, + timeRange: this.range, + widthPixels: this.resolution, // The pixel width + maxDataPoints: panel.maxDataPoints, + minInterval: panel.interval, + scopedVars: panel.scopedVars, + cacheTimeout: panel.cacheTimeout, + }); } - handleQueryResult(result) { + handleQueryResult(result: DataQueryResponse) { this.loading = false; - // check for if data source returns subject - if (result && result.subscribe) { - this.handleDataStream(result); - return; - } - if (this.dashboard.snapshot) { this.panel.snapshotData = result.data; } @@ -190,41 +217,7 @@ class MetricsPanelCtrl extends PanelCtrl { result = { data: [] }; } - // Make sure the data is TableData | TimeSeries - const data = result.data.map(v => { - if (isSeriesData(v)) { - return toLegacyResponseData(v); - } - return v; - }); - this.events.emit('data-received', data); - } - - handleDataStream(stream) { - // if we already have a connection - if (this.dataStream) { - console.log('two stream observables!'); - return; - } - - this.dataStream = stream; - this.dataSubscription = stream.subscribe({ - next: data => { - console.log('dataSubject next!'); - if (data.range) { - this.range = data.range; - } - this.events.emit('data-received', data.data); - }, - error: error => { - this.events.emit('data-error', error); - console.log('panel: observer got error'); - }, - complete: () => { - console.log('panel: observer got complete'); - this.dataStream = null; - }, - }); + this.events.emit('data-received', result.data); } getAdditionalMenuItems() { diff --git a/public/app/plugins/datasource/grafana-live/_plugin.json b/public/app/plugins/datasource/grafana-live/_plugin.json deleted file mode 100644 index 1f2ec204949..00000000000 --- a/public/app/plugins/datasource/grafana-live/_plugin.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "datasource", - "name": "Grafana Live", - "id": "grafana-live", - - "metrics": true -} diff --git a/public/app/plugins/datasource/grafana-live/datasource.ts b/public/app/plugins/datasource/grafana-live/datasource.ts deleted file mode 100644 index d861400b2c8..00000000000 --- a/public/app/plugins/datasource/grafana-live/datasource.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { liveSrv } from 'app/core/core'; - -class DataObservable { - target: any; - - constructor(target) { - this.target = target; - } - - subscribe(options) { - const observable = liveSrv.subscribe(this.target.stream); - return observable.subscribe(data => { - console.log('grafana stream ds data!', data); - }); - } -} - -export class GrafanaStreamDS { - subscription: any; - - /** @ngInject */ - constructor() {} - - query(options): any { - if (options.targets.length === 0) { - return Promise.resolve({ data: [] }); - } - - const target = options.targets[0]; - const observable = new DataObservable(target); - - return Promise.resolve(observable); - } -} diff --git a/public/app/plugins/datasource/grafana-live/module.ts b/public/app/plugins/datasource/grafana-live/module.ts deleted file mode 100644 index d75680a494b..00000000000 --- a/public/app/plugins/datasource/grafana-live/module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { GrafanaStreamDS } from './datasource'; -import { QueryCtrl } from 'app/plugins/sdk'; - -class GrafanaQueryCtrl extends QueryCtrl { - static templateUrl = 'partials/query.editor.html'; -} - -export { GrafanaStreamDS as Datasource, GrafanaQueryCtrl as QueryCtrl }; diff --git a/public/app/plugins/datasource/grafana-live/partials/query.editor.html b/public/app/plugins/datasource/grafana-live/partials/query.editor.html deleted file mode 100644 index 512263ee9ba..00000000000 --- a/public/app/plugins/datasource/grafana-live/partials/query.editor.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
- - -
-
-