mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
New solo panel route working in all scenarios I can test
This commit is contained in:
parent
c4f55fecbe
commit
aeaac7480b
@ -1,106 +1,20 @@
|
||||
import $ from 'jquery';
|
||||
import angular from 'angular';
|
||||
|
||||
export class Profiler {
|
||||
panelsRendered: number;
|
||||
enabled: boolean;
|
||||
panelsInitCount: any;
|
||||
timings: any;
|
||||
digestCounter: any;
|
||||
$rootScope: any;
|
||||
scopeCount: any;
|
||||
window: any;
|
||||
|
||||
init(config, $rootScope) {
|
||||
this.enabled = config.buildInfo.env === 'development';
|
||||
this.timings = {};
|
||||
this.timings.appStart = { loadStart: new Date().getTime() };
|
||||
this.$rootScope = $rootScope;
|
||||
this.window = window;
|
||||
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScope.$watch(
|
||||
() => {
|
||||
this.digestCounter++;
|
||||
return false;
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
|
||||
$rootScope.onAppEvent('refresh', this.refresh.bind(this), $rootScope);
|
||||
$rootScope.onAppEvent('dashboard-fetch-end', this.dashboardFetched.bind(this), $rootScope);
|
||||
$rootScope.onAppEvent('dashboard-initialized', this.dashboardInitialized.bind(this), $rootScope);
|
||||
$rootScope.onAppEvent('panel-initialized', this.panelInitialized.bind(this), $rootScope);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.timings.query = 0;
|
||||
this.timings.render = 0;
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('panel count: ' + this.panelsInitCount);
|
||||
console.log('total query: ' + this.timings.query);
|
||||
console.log('total render: ' + this.timings.render);
|
||||
console.log('avg render: ' + this.timings.render / this.panelsInitCount);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
dashboardFetched() {
|
||||
this.timings.dashboardLoadStart = new Date().getTime();
|
||||
this.panelsInitCount = 0;
|
||||
this.digestCounter = 0;
|
||||
this.panelsInitCount = 0;
|
||||
this.panelsRendered = 0;
|
||||
this.timings.query = 0;
|
||||
this.timings.render = 0;
|
||||
}
|
||||
|
||||
dashboardInitialized() {
|
||||
setTimeout(() => {
|
||||
console.log('Dashboard::Performance Total Digests: ' + this.digestCounter);
|
||||
console.log('Dashboard::Performance Total Watchers: ' + this.getTotalWatcherCount());
|
||||
console.log('Dashboard::Performance Total ScopeCount: ' + this.scopeCount);
|
||||
|
||||
const timeTaken = this.timings.lastPanelInitializedAt - this.timings.dashboardLoadStart;
|
||||
console.log('Dashboard::Performance All panels initialized in ' + timeTaken + ' ms');
|
||||
|
||||
// measure digest performance
|
||||
const rootDigestStart = window.performance.now();
|
||||
for (let i = 0; i < 30; i++) {
|
||||
this.$rootScope.$apply();
|
||||
}
|
||||
|
||||
console.log('Dashboard::Performance Root Digest ' + (window.performance.now() - rootDigestStart) / 30);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
getTotalWatcherCount() {
|
||||
let count = 0;
|
||||
let scopes = 0;
|
||||
const root = $(document.getElementsByTagName('body'));
|
||||
|
||||
const f = element => {
|
||||
if (element.data().hasOwnProperty('$scope')) {
|
||||
scopes++;
|
||||
angular.forEach(element.data().$scope.$$watchers, () => {
|
||||
count++;
|
||||
});
|
||||
}
|
||||
|
||||
angular.forEach(element.children(), childElement => {
|
||||
f($(childElement));
|
||||
});
|
||||
};
|
||||
|
||||
f(root);
|
||||
this.scopeCount = scopes;
|
||||
return count;
|
||||
}
|
||||
|
||||
renderingCompleted(panelId, panelTimings) {
|
||||
renderingCompleted(panelId) {
|
||||
// add render counter to root scope
|
||||
// used by phantomjs render.js to know when panel has rendered
|
||||
this.panelsRendered = (this.panelsRendered || 0) + 1;
|
||||
@ -108,21 +22,6 @@ export class Profiler {
|
||||
// this window variable is used by backend rendering tools to know
|
||||
// all panels have completed rendering
|
||||
this.window.panelsRendered = this.panelsRendered;
|
||||
|
||||
if (this.enabled) {
|
||||
panelTimings.renderEnd = new Date().getTime();
|
||||
this.timings.query += panelTimings.queryEnd - panelTimings.queryStart;
|
||||
this.timings.render += panelTimings.renderEnd - panelTimings.renderStart;
|
||||
}
|
||||
}
|
||||
|
||||
panelInitialized() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.panelsInitCount++;
|
||||
this.timings.lastPanelInitializedAt = new Date().getTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,21 +5,27 @@ import { connect } from 'react-redux';
|
||||
|
||||
// Utils & Services
|
||||
import appEvents from 'app/core/app_events';
|
||||
import locationUtil from 'app/core/utils/location_util';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
|
||||
// Components
|
||||
import { DashboardPanel } from '../dashgrid/DashboardPanel';
|
||||
|
||||
// Redux
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
|
||||
// Types
|
||||
import { StoreState } from 'app/types';
|
||||
import { PanelModel, DashboardModel } from 'app/features/dashboard/state';
|
||||
|
||||
interface Props {
|
||||
panelId: string;
|
||||
uid?: string;
|
||||
slug?: string;
|
||||
type?: string;
|
||||
urlUid?: string;
|
||||
urlSlug?: string;
|
||||
urlType?: string;
|
||||
$scope: any;
|
||||
$injector: any;
|
||||
updateLocation: typeof updateLocation;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@ -37,19 +43,34 @@ export class SoloPanelPage extends Component<Props, State> {
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { $injector, $scope, uid } = this.props;
|
||||
const { $injector, $scope, urlUid, urlType, urlSlug } = this.props;
|
||||
|
||||
// handle old urls with no uid
|
||||
if (!urlUid && !(urlType === 'script' || urlType === 'snapshot')) {
|
||||
this.redirectToNewUrl();
|
||||
return;
|
||||
}
|
||||
|
||||
const dashboardLoaderSrv = $injector.get('dashboardLoaderSrv');
|
||||
|
||||
// subscribe to event to know when dashboard controller is done with inititalization
|
||||
appEvents.on('dashboard-initialized', this.onDashoardInitialized);
|
||||
|
||||
dashboardLoaderSrv.loadDashboard('', '', uid).then(result => {
|
||||
dashboardLoaderSrv.loadDashboard(urlType, urlSlug, urlUid).then(result => {
|
||||
result.meta.soloMode = true;
|
||||
$scope.initDashboard(result, $scope);
|
||||
});
|
||||
}
|
||||
|
||||
redirectToNewUrl() {
|
||||
getBackendSrv().getDashboardBySlug(this.props.urlSlug).then(res => {
|
||||
if (res) {
|
||||
const url = locationUtil.stripBaseFromUrl(res.meta.url.replace('/d/', '/d-solo/'));
|
||||
this.props.updateLocation(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onDashoardInitialized = () => {
|
||||
const { $scope, panelId } = this.props;
|
||||
|
||||
@ -89,13 +110,14 @@ export class SoloPanelPage extends Component<Props, State> {
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: StoreState) => ({
|
||||
uid: state.location.routeParams.uid,
|
||||
slug: state.location.routeParams.slug,
|
||||
type: state.location.routeParams.type,
|
||||
urlUid: state.location.routeParams.uid,
|
||||
urlSlug: state.location.routeParams.slug,
|
||||
urlType: state.location.routeParams.type,
|
||||
panelId: state.location.query.panelId
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
updateLocation
|
||||
};
|
||||
|
||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(SoloPanelPage));
|
||||
|
@ -135,11 +135,8 @@ export class DataPanel extends Component<Props, State> {
|
||||
cacheTimeout: null,
|
||||
};
|
||||
|
||||
console.log('Issuing DataPanel query', queryOptions);
|
||||
const resp = await ds.query(queryOptions);
|
||||
|
||||
console.log('Issuing DataPanel query Resp', resp);
|
||||
|
||||
if (this.isUnmounted) {
|
||||
return;
|
||||
}
|
||||
|
@ -12,11 +12,12 @@ import { DataPanel } from './DataPanel';
|
||||
// Utils
|
||||
import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel';
|
||||
import { PANEL_HEADER_HEIGHT } from 'app/core/constants';
|
||||
import { profiler } from 'app/core/profiler';
|
||||
|
||||
// Types
|
||||
import { DashboardModel, PanelModel } from '../state';
|
||||
import { PanelPlugin } from 'app/types';
|
||||
import { TimeRange } from '@grafana/ui';
|
||||
import { TimeRange, LoadingState } from '@grafana/ui';
|
||||
|
||||
import variables from 'sass/_variables.scss';
|
||||
import templateSrv from 'app/features/templating/template_srv';
|
||||
@ -98,6 +99,12 @@ export class PanelChrome extends PureComponent<Props, State> {
|
||||
const { timeRange, renderCounter } = this.state;
|
||||
const PanelComponent = plugin.exports.Panel;
|
||||
|
||||
// This is only done to increase a counter that is used by backend
|
||||
// image rendering (phantomjs/headless chrome) to know when to capture image
|
||||
if (loading === LoadingState.Done) {
|
||||
profiler.renderingCompleted(panel.id);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="panel-content">
|
||||
<PanelComponent
|
||||
|
@ -1,6 +1,5 @@
|
||||
import './panel_header';
|
||||
import './panel_directive';
|
||||
import './solo_panel_ctrl';
|
||||
import './query_ctrl';
|
||||
import './panel_editor_tab';
|
||||
import './query_editor_row';
|
||||
|
@ -16,7 +16,6 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
datasourceSrv: any;
|
||||
timeSrv: any;
|
||||
templateSrv: any;
|
||||
timing: any;
|
||||
range: any;
|
||||
interval: any;
|
||||
intervalMs: any;
|
||||
@ -81,7 +80,6 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
this.loading = true;
|
||||
|
||||
// load datasource service
|
||||
this.setTimeQueryStart();
|
||||
this.datasourceSrv
|
||||
.get(this.panel.datasource)
|
||||
.then(this.updateTimeRange.bind(this))
|
||||
@ -112,14 +110,6 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
setTimeQueryStart() {
|
||||
this.timing.queryStart = new Date().getTime();
|
||||
}
|
||||
|
||||
setTimeQueryEnd() {
|
||||
this.timing.queryEnd = new Date().getTime();
|
||||
}
|
||||
|
||||
updateTimeRange(datasource?) {
|
||||
this.datasource = datasource || this.datasource;
|
||||
this.range = this.timeSrv.timeRange();
|
||||
@ -181,7 +171,6 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
}
|
||||
|
||||
handleQueryResult(result) {
|
||||
this.setTimeQueryEnd();
|
||||
this.loading = false;
|
||||
|
||||
// check for if data source returns subject
|
||||
|
@ -1,5 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import Remarkable from 'remarkable';
|
||||
|
||||
import config from 'app/core/config';
|
||||
@ -13,7 +12,7 @@ import {
|
||||
sharePanel as sharePanelUtil,
|
||||
} from 'app/features/dashboard/utils/panel';
|
||||
|
||||
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT, PANEL_HEADER_HEIGHT, PANEL_BORDER } from 'app/core/constants';
|
||||
import { GRID_COLUMN_COUNT, PANEL_HEADER_HEIGHT, PANEL_BORDER } from 'app/core/constants';
|
||||
|
||||
export class PanelCtrl {
|
||||
panel: any;
|
||||
@ -31,8 +30,8 @@ export class PanelCtrl {
|
||||
height: any;
|
||||
containerHeight: any;
|
||||
events: Emitter;
|
||||
timing: any;
|
||||
loading: boolean;
|
||||
timing: any;
|
||||
maxPanelsPerRowOptions: number[];
|
||||
|
||||
constructor($scope, $injector) {
|
||||
@ -42,7 +41,7 @@ export class PanelCtrl {
|
||||
this.$timeout = $injector.get('$timeout');
|
||||
this.editorTabs = [];
|
||||
this.events = this.panel.events;
|
||||
this.timing = {};
|
||||
this.timing = {}; // not used but here to not break plugins
|
||||
|
||||
const plugin = config.panels[this.panel.type];
|
||||
if (plugin) {
|
||||
@ -59,7 +58,7 @@ export class PanelCtrl {
|
||||
}
|
||||
|
||||
renderingCompleted() {
|
||||
profiler.renderingCompleted(this.panel.id, this.timing);
|
||||
profiler.renderingCompleted(this.panel.id);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
@ -200,24 +199,12 @@ export class PanelCtrl {
|
||||
return this.dashboard.meta.fullscreen && !this.panel.fullscreen;
|
||||
}
|
||||
|
||||
calculatePanelHeight() {
|
||||
if (this.panel.isEditing) {
|
||||
this.containerHeight = $('.panel-wrapper--edit').height();
|
||||
} else if (this.panel.fullscreen) {
|
||||
this.containerHeight = $('.panel-wrapper--view').height();
|
||||
} else {
|
||||
this.containerHeight = this.panel.gridPos.h * GRID_CELL_HEIGHT + (this.panel.gridPos.h - 1) * GRID_CELL_VMARGIN;
|
||||
}
|
||||
|
||||
if (this.panel.soloMode) {
|
||||
this.containerHeight = $(window).height();
|
||||
}
|
||||
|
||||
calculatePanelHeight(containerHeight) {
|
||||
this.containerHeight = containerHeight;
|
||||
this.height = this.containerHeight - (PANEL_BORDER + PANEL_HEADER_HEIGHT);
|
||||
}
|
||||
|
||||
render(payload?) {
|
||||
this.timing.renderStart = new Date().getTime();
|
||||
this.events.emit('render', payload);
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => {
|
||||
});
|
||||
|
||||
ctrl.events.on('panel-size-changed', () => {
|
||||
ctrl.calculatePanelHeight();
|
||||
ctrl.calculatePanelHeight(panelContainer[0].offsetHeight);
|
||||
$timeout(() => {
|
||||
resizeScrollableContent();
|
||||
ctrl.render();
|
||||
@ -112,19 +112,21 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => {
|
||||
// first wait one pass for dashboard fullscreen view mode to take effect (classses being applied)
|
||||
setTimeout(() => {
|
||||
// then recalc style
|
||||
ctrl.calculatePanelHeight();
|
||||
ctrl.calculatePanelHeight(panelContainer[0].offsetHeight);
|
||||
// then wait another cycle (this might not be needed)
|
||||
$timeout(() => {
|
||||
ctrl.render();
|
||||
resizeScrollableContent();
|
||||
});
|
||||
});
|
||||
}, 10);
|
||||
});
|
||||
|
||||
// set initial height
|
||||
ctrl.calculatePanelHeight();
|
||||
|
||||
ctrl.events.on('render', () => {
|
||||
// set initial height
|
||||
if (!ctrl.height) {
|
||||
ctrl.calculatePanelHeight(panelContainer[0].offsetHeight);
|
||||
}
|
||||
|
||||
if (transparentLastState !== ctrl.panel.transparent) {
|
||||
panelContainer.toggleClass('panel-transparent', ctrl.panel.transparent === true);
|
||||
transparentLastState = ctrl.panel.transparent;
|
||||
|
@ -1,4 +0,0 @@
|
||||
<div class="panel-solo" ng-if="panel">
|
||||
<plugin-component type="panel">
|
||||
</plugin-component>
|
||||
</div>
|
@ -1,58 +0,0 @@
|
||||
import angular from 'angular';
|
||||
import locationUtil from 'app/core/utils/location_util';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
export class SoloPanelCtrl {
|
||||
/** @ngInject */
|
||||
constructor($scope, $routeParams, $location, dashboardLoaderSrv, contextSrv, backendSrv) {
|
||||
let panelId;
|
||||
|
||||
$scope.init = () => {
|
||||
contextSrv.sidemenu = false;
|
||||
appEvents.emit('toggle-sidemenu-hidden');
|
||||
|
||||
const params = $location.search();
|
||||
panelId = parseInt(params.panelId, 10);
|
||||
|
||||
appEvents.on('dashboard-initialized', $scope.initPanelScope);
|
||||
|
||||
// if no uid, redirect to new route based on slug
|
||||
if (!($routeParams.type === 'script' || $routeParams.type === 'snapshot') && !$routeParams.uid) {
|
||||
backendSrv.getDashboardBySlug($routeParams.slug).then(res => {
|
||||
if (res) {
|
||||
const url = locationUtil.stripBaseFromUrl(res.meta.url.replace('/d/', '/d-solo/'));
|
||||
$location.path(url).replace();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug, $routeParams.uid).then(result => {
|
||||
result.meta.soloMode = true;
|
||||
$scope.initDashboard(result, $scope);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.initPanelScope = () => {
|
||||
const panelInfo = $scope.dashboard.getPanelInfoById(panelId);
|
||||
|
||||
// fake row ctrl scope
|
||||
$scope.ctrl = {
|
||||
dashboard: $scope.dashboard,
|
||||
};
|
||||
|
||||
$scope.panel = panelInfo.panel;
|
||||
$scope.panel.soloMode = true;
|
||||
$scope.$index = 0;
|
||||
|
||||
if (!$scope.panel) {
|
||||
$scope.appEvent('alert-error', ['Panel not found', '']);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('grafana.routes').controller('SoloPanelCtrl', SoloPanelCtrl);
|
@ -80,7 +80,6 @@ class TablePanelCtrl extends MetricsPanelCtrl {
|
||||
this.pageIndex = 0;
|
||||
|
||||
if (this.panel.transform === 'annotations') {
|
||||
this.setTimeQueryStart();
|
||||
return this.annotationsSrv
|
||||
.getAnnotations({
|
||||
dashboard: this.dashboard,
|
||||
|
@ -59,10 +59,11 @@ export function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
},
|
||||
})
|
||||
.when('/dashboard-solo/:type/:slug', {
|
||||
templateUrl: 'public/app/features/panel/partials/soloPanel.html',
|
||||
controller: 'SoloPanelCtrl',
|
||||
reloadOnSearch: false,
|
||||
pageClass: 'page-dashboard',
|
||||
template: '<react-container />',
|
||||
pageClass: 'dashboard-solo',
|
||||
resolve: {
|
||||
component: () => SoloPanelPage,
|
||||
},
|
||||
})
|
||||
.when('/dashboard/new', {
|
||||
templateUrl: 'public/app/partials/dashboard.html',
|
||||
|
Loading…
Reference in New Issue
Block a user