mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: jump to explore from panels with mixed datasources
- extends handlers for panel menu and keypress 'x' - in a mixed-datasource panel finds first datasource that supports explore and collects its targets - passes those targets to the found datasource to be serialized for explore state - removed `supportMetrics` and `supportsExplore` - use datasource metadata instead (set in plugin.json) - Use angular timeout to wrap url change for explore jump - Extract getExploreUrl into core/utils/explore
This commit is contained in:
@@ -4,7 +4,7 @@ import _ from 'lodash';
|
||||
import config from 'app/core/config';
|
||||
import coreModule from 'app/core/core_module';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { renderUrl } from 'app/core/utils/url';
|
||||
import { getExploreUrl } from 'app/core/utils/explore';
|
||||
|
||||
import Mousetrap from 'mousetrap';
|
||||
import 'mousetrap-global-bind';
|
||||
@@ -15,7 +15,14 @@ export class KeybindingSrv {
|
||||
timepickerOpen = false;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $rootScope, private $location, private datasourceSrv, private timeSrv, private contextSrv) {
|
||||
constructor(
|
||||
private $rootScope,
|
||||
private $location,
|
||||
private $timeout,
|
||||
private datasourceSrv,
|
||||
private timeSrv,
|
||||
private contextSrv
|
||||
) {
|
||||
// clear out all shortcuts on route change
|
||||
$rootScope.$on('$routeChangeSuccess', () => {
|
||||
Mousetrap.reset();
|
||||
@@ -194,14 +201,9 @@ export class KeybindingSrv {
|
||||
if (dashboard.meta.focusPanelId) {
|
||||
const panel = dashboard.getPanelById(dashboard.meta.focusPanelId);
|
||||
const datasource = await this.datasourceSrv.get(panel.datasource);
|
||||
if (datasource && datasource.supportsExplore) {
|
||||
const range = this.timeSrv.timeRangeForUrl();
|
||||
const state = {
|
||||
...datasource.getExploreState(panel),
|
||||
range,
|
||||
};
|
||||
const exploreState = JSON.stringify(state);
|
||||
this.$location.url(renderUrl('/explore', { state: exploreState }));
|
||||
const url = await getExploreUrl(panel, panel.targets, datasource, this.datasourceSrv, this.timeSrv);
|
||||
if (url) {
|
||||
this.$timeout(() => this.$location.url(url));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
52
public/app/core/utils/explore.ts
Normal file
52
public/app/core/utils/explore.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { renderUrl } from 'app/core/utils/url';
|
||||
|
||||
/**
|
||||
* Returns an Explore-URL that contains a panel's queries and the dashboard time range.
|
||||
*
|
||||
* @param panel Origin panel of the jump to Explore
|
||||
* @param panelTargets The origin panel's query targets
|
||||
* @param panelDatasource The origin panel's datasource
|
||||
* @param datasourceSrv Datasource service to query other datasources in case the panel datasource is mixed
|
||||
* @param timeSrv Time service to get the current dashboard range from
|
||||
*/
|
||||
export async function getExploreUrl(
|
||||
panel: any,
|
||||
panelTargets: any[],
|
||||
panelDatasource: any,
|
||||
datasourceSrv: any,
|
||||
timeSrv: any
|
||||
) {
|
||||
let exploreDatasource = panelDatasource;
|
||||
let exploreTargets = panelTargets;
|
||||
let url;
|
||||
|
||||
// Mixed datasources need to choose only one datasource
|
||||
if (panelDatasource.meta.id === 'mixed' && panelTargets) {
|
||||
// Find first explore datasource among targets
|
||||
let mixedExploreDatasource;
|
||||
for (const t of panel.targets) {
|
||||
const datasource = await datasourceSrv.get(t.datasource);
|
||||
if (datasource && datasource.meta.explore) {
|
||||
mixedExploreDatasource = datasource;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add all its targets
|
||||
if (mixedExploreDatasource) {
|
||||
exploreDatasource = mixedExploreDatasource;
|
||||
exploreTargets = panelTargets.filter(t => t.datasource === mixedExploreDatasource.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (exploreDatasource && exploreDatasource.meta.explore) {
|
||||
const range = timeSrv.timeRangeForUrl();
|
||||
const state = {
|
||||
...exploreDatasource.getExploreState(exploreTargets),
|
||||
range,
|
||||
};
|
||||
const exploreState = JSON.stringify(state);
|
||||
url = renderUrl('/explore', { state: exploreState });
|
||||
}
|
||||
return url;
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
import config from 'app/core/config';
|
||||
|
||||
// Slash encoding for angular location provider, see https://github.com/angular/angular.js/issues/10479
|
||||
const SLASH = '<SLASH>';
|
||||
export const decodePathComponent = (pc: string) => decodeURIComponent(pc).replace(new RegExp(SLASH, 'g'), '/');
|
||||
export const encodePathComponent = (pc: string) => encodeURIComponent(pc.replace(/\//g, SLASH));
|
||||
|
||||
export const stripBaseFromUrl = url => {
|
||||
const appSubUrl = config.appSubUrl;
|
||||
const stripExtraChars = appSubUrl.endsWith('/') ? 1 : 0;
|
||||
|
||||
@@ -6,7 +6,7 @@ import kbn from 'app/core/utils/kbn';
|
||||
import { PanelCtrl } from 'app/features/panel/panel_ctrl';
|
||||
import * as rangeUtil from 'app/core/utils/rangeutil';
|
||||
import * as dateMath from 'app/core/utils/datemath';
|
||||
import { renderUrl } from 'app/core/utils/url';
|
||||
import { getExploreUrl } from 'app/core/utils/explore';
|
||||
|
||||
import { metricsTabDirective } from './metrics_tab';
|
||||
|
||||
@@ -314,7 +314,12 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
|
||||
getAdditionalMenuItems() {
|
||||
const items = [];
|
||||
if (config.exploreEnabled && this.contextSrv.isEditor && this.datasource && this.datasource.supportsExplore) {
|
||||
if (
|
||||
config.exploreEnabled &&
|
||||
this.contextSrv.isEditor &&
|
||||
this.datasource &&
|
||||
(this.datasource.meta.explore || this.datasource.meta.id === 'mixed')
|
||||
) {
|
||||
items.push({
|
||||
text: 'Explore',
|
||||
click: 'ctrl.explore();',
|
||||
@@ -325,14 +330,11 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
return items;
|
||||
}
|
||||
|
||||
explore() {
|
||||
const range = this.timeSrv.timeRangeForUrl();
|
||||
const state = {
|
||||
...this.datasource.getExploreState(this.panel),
|
||||
range,
|
||||
};
|
||||
const exploreState = JSON.stringify(state);
|
||||
this.$location.url(renderUrl('/explore', { state: exploreState }));
|
||||
async explore() {
|
||||
const url = await getExploreUrl(this.panel, this.panel.targets, this.datasource, this.datasourceSrv, this.timeSrv);
|
||||
if (url) {
|
||||
this.$timeout(() => this.$location.url(url));
|
||||
}
|
||||
}
|
||||
|
||||
addQuery(target) {
|
||||
|
||||
@@ -38,7 +38,7 @@ describe('MetricsPanelCtrl', () => {
|
||||
describe('and has datasource set that supports explore and user has powers', () => {
|
||||
beforeEach(() => {
|
||||
ctrl.contextSrv = { isEditor: true };
|
||||
ctrl.datasource = { supportsExplore: true };
|
||||
ctrl.datasource = { meta: { explore: true } };
|
||||
additionalItems = ctrl.getAdditionalMenuItems();
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import * as templatingVariable from 'app/features/templating/variable';
|
||||
export default class CloudWatchDatasource {
|
||||
type: any;
|
||||
name: any;
|
||||
supportMetrics: any;
|
||||
proxyUrl: any;
|
||||
defaultRegion: any;
|
||||
instanceSettings: any;
|
||||
@@ -17,7 +16,6 @@ export default class CloudWatchDatasource {
|
||||
constructor(instanceSettings, private $q, private backendSrv, private templateSrv, private timeSrv) {
|
||||
this.type = 'cloudwatch';
|
||||
this.name = instanceSettings.name;
|
||||
this.supportMetrics = true;
|
||||
this.proxyUrl = instanceSettings.url;
|
||||
this.defaultRegion = instanceSettings.jsonData.defaultRegion;
|
||||
this.instanceSettings = instanceSettings;
|
||||
|
||||
@@ -16,8 +16,6 @@ export default class InfluxDatasource {
|
||||
basicAuth: any;
|
||||
withCredentials: any;
|
||||
interval: any;
|
||||
supportAnnotations: boolean;
|
||||
supportMetrics: boolean;
|
||||
responseParser: any;
|
||||
|
||||
/** @ngInject */
|
||||
@@ -34,8 +32,6 @@ export default class InfluxDatasource {
|
||||
this.basicAuth = instanceSettings.basicAuth;
|
||||
this.withCredentials = instanceSettings.withCredentials;
|
||||
this.interval = (instanceSettings.jsonData || {}).timeInterval;
|
||||
this.supportAnnotations = true;
|
||||
this.supportMetrics = true;
|
||||
this.responseParser = new ResponseParser();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ export default class OpenTsDatasource {
|
||||
basicAuth: any;
|
||||
tsdbVersion: any;
|
||||
tsdbResolution: any;
|
||||
supportMetrics: any;
|
||||
tagKeys: any;
|
||||
|
||||
aggregatorsPromise: any;
|
||||
@@ -26,7 +25,6 @@ export default class OpenTsDatasource {
|
||||
instanceSettings.jsonData = instanceSettings.jsonData || {};
|
||||
this.tsdbVersion = instanceSettings.jsonData.tsdbVersion || 1;
|
||||
this.tsdbResolution = instanceSettings.jsonData.tsdbResolution || 1;
|
||||
this.supportMetrics = true;
|
||||
this.tagKeys = {};
|
||||
|
||||
this.aggregatorsPromise = null;
|
||||
|
||||
@@ -149,8 +149,6 @@ export class PrometheusDatasource {
|
||||
editorSrc: string;
|
||||
name: string;
|
||||
ruleMappings: { [index: string]: string };
|
||||
supportsExplore: boolean;
|
||||
supportMetrics: boolean;
|
||||
url: string;
|
||||
directUrl: string;
|
||||
basicAuth: any;
|
||||
@@ -166,8 +164,6 @@ export class PrometheusDatasource {
|
||||
this.type = 'prometheus';
|
||||
this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
|
||||
this.name = instanceSettings.name;
|
||||
this.supportsExplore = true;
|
||||
this.supportMetrics = true;
|
||||
this.url = instanceSettings.url;
|
||||
this.directUrl = instanceSettings.directUrl;
|
||||
this.basicAuth = instanceSettings.basicAuth;
|
||||
@@ -522,10 +518,10 @@ export class PrometheusDatasource {
|
||||
});
|
||||
}
|
||||
|
||||
getExploreState(panel) {
|
||||
getExploreState(targets: any[]) {
|
||||
let state = {};
|
||||
if (panel.targets) {
|
||||
const queries = panel.targets.map(t => ({
|
||||
if (targets && targets.length > 0) {
|
||||
const queries = targets.map(t => ({
|
||||
query: this.templateSrv.replace(t.expr, {}, this.interpolateQueryExpr),
|
||||
format: t.format,
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user