mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(live): progress on panel <-> data source communication patterns
This commit is contained in:
@@ -29,6 +29,7 @@ import {colorPicker} from './components/colorpicker';
|
|||||||
import {navbarDirective} from './components/navbar/navbar';
|
import {navbarDirective} from './components/navbar/navbar';
|
||||||
import {arrayJoin} from './directives/array_join';
|
import {arrayJoin} from './directives/array_join';
|
||||||
import {liveSrv} from './live/live_srv';
|
import {liveSrv} from './live/live_srv';
|
||||||
|
import {Emitter} from './utils/emitter';
|
||||||
import {layoutSelector} from './components/layout_selector/layout_selector';
|
import {layoutSelector} from './components/layout_selector/layout_selector';
|
||||||
import 'app/core/controllers/all';
|
import 'app/core/controllers/all';
|
||||||
import 'app/core/services/all';
|
import 'app/core/services/all';
|
||||||
@@ -46,5 +47,6 @@ export {
|
|||||||
colorPicker,
|
colorPicker,
|
||||||
liveSrv,
|
liveSrv,
|
||||||
layoutSelector,
|
layoutSelector,
|
||||||
infoPopover
|
infoPopover,
|
||||||
|
Emitter
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -146,11 +146,11 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// ConfigCtrl
|
// Datasource ConfigCtrl
|
||||||
case 'datasource-config-ctrl': {
|
case 'datasource-config-ctrl': {
|
||||||
var dsMeta = scope.ctrl.datasourceMeta;
|
var dsMeta = scope.ctrl.datasourceMeta;
|
||||||
return System.import(dsMeta.module).then(function(dsModule): any {
|
return System.import(dsMeta.module).then(function(dsModule): any {
|
||||||
if (!dsMeta.ConfigCtrl) {
|
if (!dsModule.ConfigCtrl) {
|
||||||
return {notFound: true};
|
return {notFound: true};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
public/app/core/utils/emitter.ts
Normal file
47
public/app/core/utils/emitter.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
import {Subject} from 'vendor/npm/rxjs/Subject';
|
||||||
|
|
||||||
|
var hasOwnProp = {}.hasOwnProperty;
|
||||||
|
|
||||||
|
function createName(name) {
|
||||||
|
return '$' + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Emitter {
|
||||||
|
subjects: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.subjects = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(name, data) {
|
||||||
|
var fnName = createName(name);
|
||||||
|
this.subjects[fnName] || (this.subjects[fnName] = new Subject());
|
||||||
|
this.subjects[fnName].next(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
on(name, handler) {
|
||||||
|
var fnName = createName(name);
|
||||||
|
this.subjects[fnName] || (this.subjects[fnName] = new Subject());
|
||||||
|
this.subjects[fnName].subscribe(handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
off(name, handler) {
|
||||||
|
var fnName = createName(name);
|
||||||
|
if (this.subjects[fnName]) {
|
||||||
|
this.subjects[fnName].dispose();
|
||||||
|
delete this.subjects[fnName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
var subjects = this.subjects;
|
||||||
|
for (var prop in subjects) {
|
||||||
|
if (hasOwnProp.call(subjects, prop)) {
|
||||||
|
subjects[prop].dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.subjects = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ import {PanelCtrl} from './panel_ctrl';
|
|||||||
import * as rangeUtil from 'app/core/utils/rangeutil';
|
import * as rangeUtil from 'app/core/utils/rangeutil';
|
||||||
import * as dateMath from 'app/core/utils/datemath';
|
import * as dateMath from 'app/core/utils/datemath';
|
||||||
|
|
||||||
|
import {Subject} from 'vendor/npm/rxjs/Subject';
|
||||||
|
|
||||||
class MetricsPanelCtrl extends PanelCtrl {
|
class MetricsPanelCtrl extends PanelCtrl {
|
||||||
error: boolean;
|
error: boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@@ -26,7 +28,8 @@ class MetricsPanelCtrl extends PanelCtrl {
|
|||||||
timeInfo: any;
|
timeInfo: any;
|
||||||
skipDataOnInit: boolean;
|
skipDataOnInit: boolean;
|
||||||
datasources: any[];
|
datasources: any[];
|
||||||
dataSubject: any;
|
dataStream: any;
|
||||||
|
dataSubscription: any;
|
||||||
|
|
||||||
constructor($scope, $injector) {
|
constructor($scope, $injector) {
|
||||||
super($scope, $injector);
|
super($scope, $injector);
|
||||||
@@ -50,11 +53,6 @@ class MetricsPanelCtrl extends PanelCtrl {
|
|||||||
this.datasources = this.datasourceSrv.getMetricSources();
|
this.datasources = this.datasourceSrv.getMetricSources();
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshData(data) {
|
|
||||||
// null op
|
|
||||||
return this.$q.when(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSnapshot(data) {
|
loadSnapshot(data) {
|
||||||
// null op
|
// null op
|
||||||
return data;
|
return data;
|
||||||
@@ -73,21 +71,27 @@ class MetricsPanelCtrl extends PanelCtrl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // ignore if we have data stream
|
||||||
|
if (this.dataStream) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// clear loading/error state
|
// clear loading/error state
|
||||||
delete this.error;
|
delete this.error;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
// load datasource service
|
// load datasource service
|
||||||
this.datasourceSrv.get(this.panel.datasource).then(datasource => {
|
this.datasourceSrv.get(this.panel.datasource)
|
||||||
this.datasource = datasource;
|
.then(this.issueQueries.bind(this))
|
||||||
return this.refreshData(this.datasource);
|
.then(() => {
|
||||||
}).then(() => {
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.log('Panel data error:', err);
|
console.log('Panel data error:', err);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.error = err.message || "Timeseries data request error";
|
this.error = err.message || "Timeseries data request error";
|
||||||
this.inspector = {error: err};
|
this.inspector = {error: err};
|
||||||
|
|
||||||
|
this.events.emit('data-error', err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,10 +171,6 @@ class MetricsPanelCtrl extends PanelCtrl {
|
|||||||
return this.$q.when([]);
|
return this.$q.when([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.dataSubject) {
|
|
||||||
return this.$q.when([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var metricsQuery = {
|
var metricsQuery = {
|
||||||
panelId: this.panel.id,
|
panelId: this.panel.id,
|
||||||
range: this.range,
|
range: this.range,
|
||||||
@@ -190,15 +190,15 @@ class MetricsPanelCtrl extends PanelCtrl {
|
|||||||
|
|
||||||
// check for if data source returns subject
|
// check for if data source returns subject
|
||||||
if (results && results.subscribe) {
|
if (results && results.subscribe) {
|
||||||
this.handleDataSubject(results);
|
this.handleDataStream(results);
|
||||||
return {data: []};
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.dashboard.snapshot) {
|
if (this.dashboard.snapshot) {
|
||||||
this.panel.snapshotData = results;
|
this.panel.snapshotData = results;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.dataHandler(results);
|
return this.events.emit('data-received', results);
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return this.$q.reject(err);
|
return this.$q.reject(err);
|
||||||
@@ -209,22 +209,24 @@ class MetricsPanelCtrl extends PanelCtrl {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDataSubject(subject) {
|
handleDataStream(stream) {
|
||||||
// if we already have a connection
|
// if we already have a connection
|
||||||
if (this.dataSubject) {
|
if (this.dataStream) {
|
||||||
|
console.log('two stream observables!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dataSubject = subject;
|
this.dataStream = stream;
|
||||||
this.dataSubject.subscribe({
|
this.dataSubscription = stream.subscribe({
|
||||||
next: (data) => {
|
next: (data) => {
|
||||||
console.log('dataSubject next!');
|
console.log('dataSubject next!');
|
||||||
if (data.range) {
|
if (data.range) {
|
||||||
this.range = data.range;
|
this.range = data.range;
|
||||||
}
|
}
|
||||||
this.dataHandler(data);
|
this.events.emit('data-received', data);
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
|
this.events.emit('data-error', error);
|
||||||
console.log('panel: observer got error');
|
console.log('panel: observer got error');
|
||||||
},
|
},
|
||||||
complete: () => {
|
complete: () => {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ const TITLE_HEIGHT = 25;
|
|||||||
const EMPTY_TITLE_HEIGHT = 9;
|
const EMPTY_TITLE_HEIGHT = 9;
|
||||||
const PANEL_PADDING = 5;
|
const PANEL_PADDING = 5;
|
||||||
|
|
||||||
|
import {Emitter} from 'app/core/core';
|
||||||
|
|
||||||
export class PanelCtrl {
|
export class PanelCtrl {
|
||||||
panel: any;
|
panel: any;
|
||||||
row: any;
|
row: any;
|
||||||
@@ -28,12 +30,14 @@ export class PanelCtrl {
|
|||||||
editMode: any;
|
editMode: any;
|
||||||
height: any;
|
height: any;
|
||||||
containerHeight: any;
|
containerHeight: any;
|
||||||
|
events: Emitter;
|
||||||
|
|
||||||
constructor($scope, $injector) {
|
constructor($scope, $injector) {
|
||||||
this.$injector = $injector;
|
this.$injector = $injector;
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.$timeout = $injector.get('$timeout');
|
this.$timeout = $injector.get('$timeout');
|
||||||
this.editorTabIndex = 0;
|
this.editorTabIndex = 0;
|
||||||
|
this.events = new Emitter();
|
||||||
|
|
||||||
var plugin = config.panels[this.panel.type];
|
var plugin = config.panels[this.panel.type];
|
||||||
if (plugin) {
|
if (plugin) {
|
||||||
@@ -56,7 +60,7 @@ export class PanelCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
return;
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
publishAppEvent(evtName, evt) {
|
publishAppEvent(evtName, evt) {
|
||||||
@@ -138,7 +142,7 @@ export class PanelCtrl {
|
|||||||
this.height = this.containerHeight - (PANEL_PADDING + (this.panel.title ? TITLE_HEIGHT : EMPTY_TITLE_HEIGHT));
|
this.height = this.containerHeight - (PANEL_PADDING + (this.panel.title ? TITLE_HEIGHT : EMPTY_TITLE_HEIGHT));
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastRender(arg1?, arg2?) {
|
render(arg1?, arg2?) {
|
||||||
this.$scope.$broadcast('render', arg1, arg2);
|
this.$scope.$broadcast('render', arg1, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +161,7 @@ export class PanelCtrl {
|
|||||||
updateColumnSpan(span) {
|
updateColumnSpan(span) {
|
||||||
this.panel.span = Math.min(Math.max(Math.floor(this.panel.span + span), 1), 12);
|
this.panel.span = Math.min(Math.max(Math.floor(this.panel.span + span), 1), 12);
|
||||||
this.$timeout(() => {
|
this.$timeout(() => {
|
||||||
this.broadcastRender();
|
this.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export class GrafanaStreamDS {
|
|||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query(options): any {
|
query(options): any {
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ class GraphCtrl extends MetricsPanelCtrl {
|
|||||||
_.defaults(this.panel.legend, panelDefaults.legend);
|
_.defaults(this.panel.legend, panelDefaults.legend);
|
||||||
|
|
||||||
this.colors = $scope.$root.colors;
|
this.colors = $scope.$root.colors;
|
||||||
|
|
||||||
|
this.events.on('data-received', this.onDataReceived.bind(this));
|
||||||
|
this.events.on('data-error', this.onDataError.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
initEditMode() {
|
initEditMode() {
|
||||||
@@ -138,14 +141,9 @@ class GraphCtrl extends MetricsPanelCtrl {
|
|||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshData(datasource) {
|
issueQueries(datasource) {
|
||||||
this.annotationsPromise = this.annotationsSrv.getAnnotations(this.dashboard);
|
this.annotationsPromise = this.annotationsSrv.getAnnotations(this.dashboard);
|
||||||
|
return super.issueQueries(datasource);
|
||||||
return this.issueQueries(datasource).catch(err => {
|
|
||||||
this.seriesList = [];
|
|
||||||
this.render([]);
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
zoomOut(evt) {
|
zoomOut(evt) {
|
||||||
@@ -157,7 +155,12 @@ class GraphCtrl extends MetricsPanelCtrl {
|
|||||||
this.dataHandler(snapshotData);
|
this.dataHandler(snapshotData);
|
||||||
}
|
}
|
||||||
|
|
||||||
dataHandler(results) {
|
onDataError(err) {
|
||||||
|
this.seriesList = [];
|
||||||
|
this.render([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDataReceived(results) {
|
||||||
// png renderer returns just a url
|
// png renderer returns just a url
|
||||||
if (_.isString(results)) {
|
if (_.isString(results)) {
|
||||||
this.render(results);
|
this.render(results);
|
||||||
@@ -178,7 +181,7 @@ class GraphCtrl extends MetricsPanelCtrl {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.render(this.seriesList);
|
this.render(this.seriesList);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
seriesHandler(seriesData, index) {
|
seriesHandler(seriesData, index) {
|
||||||
var datapoints = seriesData.datapoints;
|
var datapoints = seriesData.datapoints;
|
||||||
@@ -208,10 +211,6 @@ class GraphCtrl extends MetricsPanelCtrl {
|
|||||||
return series;
|
return series;
|
||||||
}
|
}
|
||||||
|
|
||||||
render(data?: any) {
|
|
||||||
this.broadcastRender(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
changeSeriesColor(series, color) {
|
changeSeriesColor(series, color) {
|
||||||
series.color = color;
|
series.color = color;
|
||||||
this.panel.aliasColors[series.alias] = series.color;
|
this.panel.aliasColors[series.alias] = series.color;
|
||||||
|
|||||||
@@ -80,9 +80,7 @@ class TablePanelCtrl extends MetricsPanelCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.issueQueries(datasource)
|
return this.issueQueries(datasource).catch(err => {
|
||||||
.then(this.dataHandler.bind(this))
|
|
||||||
.catch(err => {
|
|
||||||
this.render();
|
this.render();
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,11 +29,9 @@ export class TextPanelCtrl extends PanelCtrl {
|
|||||||
this.editorTabIndex = 1;
|
this.editorTabIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
super.render();
|
||||||
|
|
||||||
if (this.panel.mode === 'markdown') {
|
if (this.panel.mode === 'markdown') {
|
||||||
this.renderMarkdown(this.panel.content);
|
this.renderMarkdown(this.panel.content);
|
||||||
} else if (this.panel.mode === 'html') {
|
} else if (this.panel.mode === 'html') {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export class UnknownPanelCtrl extends PanelCtrl {
|
|||||||
constructor($scope, $injector) {
|
constructor($scope, $injector) {
|
||||||
super($scope, $injector);
|
super($scope, $injector);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user