Merge pull request #13416 from grafana/davkal/11999-explore-from-mixed-panels
Explore: jump to explore from panels with mixed datasources
This commit is contained in:
@@ -4,7 +4,7 @@ import _ from 'lodash';
|
|||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
import appEvents from 'app/core/app_events';
|
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 from 'mousetrap';
|
||||||
import 'mousetrap-global-bind';
|
import 'mousetrap-global-bind';
|
||||||
@@ -15,7 +15,14 @@ export class KeybindingSrv {
|
|||||||
timepickerOpen = false;
|
timepickerOpen = false;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @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
|
// clear out all shortcuts on route change
|
||||||
$rootScope.$on('$routeChangeSuccess', () => {
|
$rootScope.$on('$routeChangeSuccess', () => {
|
||||||
Mousetrap.reset();
|
Mousetrap.reset();
|
||||||
@@ -194,14 +201,9 @@ export class KeybindingSrv {
|
|||||||
if (dashboard.meta.focusPanelId) {
|
if (dashboard.meta.focusPanelId) {
|
||||||
const panel = dashboard.getPanelById(dashboard.meta.focusPanelId);
|
const panel = dashboard.getPanelById(dashboard.meta.focusPanelId);
|
||||||
const datasource = await this.datasourceSrv.get(panel.datasource);
|
const datasource = await this.datasourceSrv.get(panel.datasource);
|
||||||
if (datasource && datasource.supportsExplore) {
|
const url = await getExploreUrl(panel, panel.targets, datasource, this.datasourceSrv, this.timeSrv);
|
||||||
const range = this.timeSrv.timeRangeForUrl();
|
if (url) {
|
||||||
const state = {
|
this.$timeout(() => this.$location.url(url));
|
||||||
...datasource.getExploreState(panel),
|
|
||||||
range,
|
|
||||||
};
|
|
||||||
const exploreState = JSON.stringify(state);
|
|
||||||
this.$location.url(renderUrl('/explore', { state: exploreState }));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+4
-5
@@ -1,6 +1,5 @@
|
|||||||
import { serializeStateToUrlParam, parseUrlState } from './Wrapper';
|
import { DEFAULT_RANGE, serializeStateToUrlParam, parseUrlState } from './explore';
|
||||||
import { DEFAULT_RANGE } from './TimePicker';
|
import { ExploreState } from 'app/types/explore';
|
||||||
import { ExploreState } from './Explore';
|
|
||||||
|
|
||||||
const DEFAULT_EXPLORE_STATE: ExploreState = {
|
const DEFAULT_EXPLORE_STATE: ExploreState = {
|
||||||
datasource: null,
|
datasource: null,
|
||||||
@@ -27,7 +26,7 @@ const DEFAULT_EXPLORE_STATE: ExploreState = {
|
|||||||
tableResult: null,
|
tableResult: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Wrapper state functions', () => {
|
describe('state functions', () => {
|
||||||
describe('parseUrlState', () => {
|
describe('parseUrlState', () => {
|
||||||
it('returns default state on empty string', () => {
|
it('returns default state on empty string', () => {
|
||||||
expect(parseUrlState('')).toMatchObject({
|
expect(parseUrlState('')).toMatchObject({
|
||||||
@@ -57,7 +56,7 @@ describe('Wrapper state functions', () => {
|
|||||||
};
|
};
|
||||||
expect(serializeStateToUrlParam(state)).toBe(
|
expect(serializeStateToUrlParam(state)).toBe(
|
||||||
'{"datasource":"foo","queries":[{"query":"metric{test=\\"a/b\\"}"},' +
|
'{"datasource":"foo","queries":[{"query":"metric{test=\\"a/b\\"}"},' +
|
||||||
'{"query":"super{foo=\\"x/z\\"}"}],"range":{"from":"now - 5h","to":"now"}}'
|
'{"query":"super{foo=\\"x/z\\"}"}],"range":{"from":"now - 5h","to":"now"}}'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { renderUrl } from 'app/core/utils/url';
|
||||||
|
import { ExploreState, ExploreUrlState } from 'app/types/explore';
|
||||||
|
|
||||||
|
export const DEFAULT_RANGE = {
|
||||||
|
from: 'now-6h',
|
||||||
|
to: 'now',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseUrlState(initial: string | undefined): ExploreUrlState {
|
||||||
|
if (initial) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(decodeURI(initial));
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { datasource: null, queries: [], range: DEFAULT_RANGE };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function serializeStateToUrlParam(state: ExploreState): string {
|
||||||
|
const urlState: ExploreUrlState = {
|
||||||
|
datasource: state.datasourceName,
|
||||||
|
queries: state.queries.map(q => ({ query: q.query })),
|
||||||
|
range: state.range,
|
||||||
|
};
|
||||||
|
return JSON.stringify(urlState);
|
||||||
|
}
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
import config from 'app/core/config';
|
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 => {
|
export const stripBaseFromUrl = url => {
|
||||||
const appSubUrl = config.appSubUrl;
|
const appSubUrl = config.appSubUrl;
|
||||||
const stripExtraChars = appSubUrl.endsWith('/') ? 1 : 0;
|
const stripExtraChars = appSubUrl.endsWith('/') ? 1 : 0;
|
||||||
|
|||||||
@@ -2,19 +2,20 @@ import React from 'react';
|
|||||||
import { hot } from 'react-hot-loader';
|
import { hot } from 'react-hot-loader';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
|
|
||||||
import { Query, Range, ExploreUrlState } from 'app/types/explore';
|
import { ExploreState, ExploreUrlState } from 'app/types/explore';
|
||||||
import kbn from 'app/core/utils/kbn';
|
import kbn from 'app/core/utils/kbn';
|
||||||
import colors from 'app/core/utils/colors';
|
import colors from 'app/core/utils/colors';
|
||||||
import store from 'app/core/store';
|
import store from 'app/core/store';
|
||||||
import TimeSeries from 'app/core/time_series2';
|
import TimeSeries from 'app/core/time_series2';
|
||||||
import { parse as parseDate } from 'app/core/utils/datemath';
|
import { parse as parseDate } from 'app/core/utils/datemath';
|
||||||
|
import { DEFAULT_RANGE } from 'app/core/utils/explore';
|
||||||
|
|
||||||
import ElapsedTime from './ElapsedTime';
|
import ElapsedTime from './ElapsedTime';
|
||||||
import QueryRows from './QueryRows';
|
import QueryRows from './QueryRows';
|
||||||
import Graph from './Graph';
|
import Graph from './Graph';
|
||||||
import Logs from './Logs';
|
import Logs from './Logs';
|
||||||
import Table from './Table';
|
import Table from './Table';
|
||||||
import TimePicker, { DEFAULT_RANGE } from './TimePicker';
|
import TimePicker from './TimePicker';
|
||||||
import { ensureQueries, generateQueryKey, hasQuery } from './utils/query';
|
import { ensureQueries, generateQueryKey, hasQuery } from './utils/query';
|
||||||
|
|
||||||
const MAX_HISTORY_ITEMS = 100;
|
const MAX_HISTORY_ITEMS = 100;
|
||||||
@@ -58,31 +59,6 @@ interface ExploreProps {
|
|||||||
urlState: ExploreUrlState;
|
urlState: ExploreUrlState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExploreState {
|
|
||||||
datasource: any;
|
|
||||||
datasourceError: any;
|
|
||||||
datasourceLoading: boolean | null;
|
|
||||||
datasourceMissing: boolean;
|
|
||||||
datasourceName?: string;
|
|
||||||
graphResult: any;
|
|
||||||
history: any[];
|
|
||||||
latency: number;
|
|
||||||
loading: any;
|
|
||||||
logsResult: any;
|
|
||||||
queries: Query[];
|
|
||||||
queryErrors: any[];
|
|
||||||
queryHints: any[];
|
|
||||||
range: Range;
|
|
||||||
requestOptions: any;
|
|
||||||
showingGraph: boolean;
|
|
||||||
showingLogs: boolean;
|
|
||||||
showingTable: boolean;
|
|
||||||
supportsGraph: boolean | null;
|
|
||||||
supportsLogs: boolean | null;
|
|
||||||
supportsTable: boolean | null;
|
|
||||||
tableResult: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||||
el: any;
|
el: any;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import * as dateMath from 'app/core/utils/datemath';
|
|||||||
import * as rangeUtil from 'app/core/utils/rangeutil';
|
import * as rangeUtil from 'app/core/utils/rangeutil';
|
||||||
|
|
||||||
const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
||||||
|
|
||||||
export const DEFAULT_RANGE = {
|
export const DEFAULT_RANGE = {
|
||||||
from: 'now-6h',
|
from: 'now-6h',
|
||||||
to: 'now',
|
to: 'now',
|
||||||
|
|||||||
@@ -3,31 +3,11 @@ import { hot } from 'react-hot-loader';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { updateLocation } from 'app/core/actions';
|
import { updateLocation } from 'app/core/actions';
|
||||||
|
import { serializeStateToUrlParam, parseUrlState } from 'app/core/utils/explore';
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
import { ExploreUrlState } from 'app/types/explore';
|
import { ExploreState } from 'app/types/explore';
|
||||||
|
|
||||||
import Explore, { ExploreState } from './Explore';
|
import Explore from './Explore';
|
||||||
import { DEFAULT_RANGE } from './TimePicker';
|
|
||||||
|
|
||||||
export function parseUrlState(initial: string | undefined): ExploreUrlState {
|
|
||||||
if (initial) {
|
|
||||||
try {
|
|
||||||
return JSON.parse(decodeURI(initial));
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { datasource: null, queries: [], range: DEFAULT_RANGE };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serializeStateToUrlParam(state: ExploreState): string {
|
|
||||||
const urlState: ExploreUrlState = {
|
|
||||||
datasource: state.datasourceName,
|
|
||||||
queries: state.queries.map(q => ({ query: q.query })),
|
|
||||||
range: state.range,
|
|
||||||
};
|
|
||||||
return JSON.stringify(urlState);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WrapperProps {
|
interface WrapperProps {
|
||||||
backendSrv?: any;
|
backendSrv?: any;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import kbn from 'app/core/utils/kbn';
|
|||||||
import { PanelCtrl } from 'app/features/panel/panel_ctrl';
|
import { PanelCtrl } from 'app/features/panel/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 { renderUrl } from 'app/core/utils/url';
|
import { getExploreUrl } from 'app/core/utils/explore';
|
||||||
|
|
||||||
import { metricsTabDirective } from './metrics_tab';
|
import { metricsTabDirective } from './metrics_tab';
|
||||||
|
|
||||||
@@ -314,7 +314,12 @@ class MetricsPanelCtrl extends PanelCtrl {
|
|||||||
|
|
||||||
getAdditionalMenuItems() {
|
getAdditionalMenuItems() {
|
||||||
const items = [];
|
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({
|
items.push({
|
||||||
text: 'Explore',
|
text: 'Explore',
|
||||||
click: 'ctrl.explore();',
|
click: 'ctrl.explore();',
|
||||||
@@ -325,14 +330,11 @@ class MetricsPanelCtrl extends PanelCtrl {
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
explore() {
|
async explore() {
|
||||||
const range = this.timeSrv.timeRangeForUrl();
|
const url = await getExploreUrl(this.panel, this.panel.targets, this.datasource, this.datasourceSrv, this.timeSrv);
|
||||||
const state = {
|
if (url) {
|
||||||
...this.datasource.getExploreState(this.panel),
|
this.$timeout(() => this.$location.url(url));
|
||||||
range,
|
}
|
||||||
};
|
|
||||||
const exploreState = JSON.stringify(state);
|
|
||||||
this.$location.url(renderUrl('/explore', { state: exploreState }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addQuery(target) {
|
addQuery(target) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ describe('MetricsPanelCtrl', () => {
|
|||||||
describe('and has datasource set that supports explore and user has powers', () => {
|
describe('and has datasource set that supports explore and user has powers', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ctrl.contextSrv = { isEditor: true };
|
ctrl.contextSrv = { isEditor: true };
|
||||||
ctrl.datasource = { supportsExplore: true };
|
ctrl.datasource = { meta: { explore: true } };
|
||||||
additionalItems = ctrl.getAdditionalMenuItems();
|
additionalItems = ctrl.getAdditionalMenuItems();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import * as templatingVariable from 'app/features/templating/variable';
|
|||||||
export default class CloudWatchDatasource {
|
export default class CloudWatchDatasource {
|
||||||
type: any;
|
type: any;
|
||||||
name: any;
|
name: any;
|
||||||
supportMetrics: any;
|
|
||||||
proxyUrl: any;
|
proxyUrl: any;
|
||||||
defaultRegion: any;
|
defaultRegion: any;
|
||||||
instanceSettings: any;
|
instanceSettings: any;
|
||||||
@@ -17,7 +16,6 @@ export default class CloudWatchDatasource {
|
|||||||
constructor(instanceSettings, private $q, private backendSrv, private templateSrv, private timeSrv) {
|
constructor(instanceSettings, private $q, private backendSrv, private templateSrv, private timeSrv) {
|
||||||
this.type = 'cloudwatch';
|
this.type = 'cloudwatch';
|
||||||
this.name = instanceSettings.name;
|
this.name = instanceSettings.name;
|
||||||
this.supportMetrics = true;
|
|
||||||
this.proxyUrl = instanceSettings.url;
|
this.proxyUrl = instanceSettings.url;
|
||||||
this.defaultRegion = instanceSettings.jsonData.defaultRegion;
|
this.defaultRegion = instanceSettings.jsonData.defaultRegion;
|
||||||
this.instanceSettings = instanceSettings;
|
this.instanceSettings = instanceSettings;
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ export default class InfluxDatasource {
|
|||||||
basicAuth: any;
|
basicAuth: any;
|
||||||
withCredentials: any;
|
withCredentials: any;
|
||||||
interval: any;
|
interval: any;
|
||||||
supportAnnotations: boolean;
|
|
||||||
supportMetrics: boolean;
|
|
||||||
responseParser: any;
|
responseParser: any;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
@@ -34,8 +32,6 @@ export default class InfluxDatasource {
|
|||||||
this.basicAuth = instanceSettings.basicAuth;
|
this.basicAuth = instanceSettings.basicAuth;
|
||||||
this.withCredentials = instanceSettings.withCredentials;
|
this.withCredentials = instanceSettings.withCredentials;
|
||||||
this.interval = (instanceSettings.jsonData || {}).timeInterval;
|
this.interval = (instanceSettings.jsonData || {}).timeInterval;
|
||||||
this.supportAnnotations = true;
|
|
||||||
this.supportMetrics = true;
|
|
||||||
this.responseParser = new ResponseParser();
|
this.responseParser = new ResponseParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ export default class OpenTsDatasource {
|
|||||||
basicAuth: any;
|
basicAuth: any;
|
||||||
tsdbVersion: any;
|
tsdbVersion: any;
|
||||||
tsdbResolution: any;
|
tsdbResolution: any;
|
||||||
supportMetrics: any;
|
|
||||||
tagKeys: any;
|
tagKeys: any;
|
||||||
|
|
||||||
aggregatorsPromise: any;
|
aggregatorsPromise: any;
|
||||||
@@ -26,7 +25,6 @@ export default class OpenTsDatasource {
|
|||||||
instanceSettings.jsonData = instanceSettings.jsonData || {};
|
instanceSettings.jsonData = instanceSettings.jsonData || {};
|
||||||
this.tsdbVersion = instanceSettings.jsonData.tsdbVersion || 1;
|
this.tsdbVersion = instanceSettings.jsonData.tsdbVersion || 1;
|
||||||
this.tsdbResolution = instanceSettings.jsonData.tsdbResolution || 1;
|
this.tsdbResolution = instanceSettings.jsonData.tsdbResolution || 1;
|
||||||
this.supportMetrics = true;
|
|
||||||
this.tagKeys = {};
|
this.tagKeys = {};
|
||||||
|
|
||||||
this.aggregatorsPromise = null;
|
this.aggregatorsPromise = null;
|
||||||
|
|||||||
@@ -149,8 +149,6 @@ export class PrometheusDatasource {
|
|||||||
editorSrc: string;
|
editorSrc: string;
|
||||||
name: string;
|
name: string;
|
||||||
ruleMappings: { [index: string]: string };
|
ruleMappings: { [index: string]: string };
|
||||||
supportsExplore: boolean;
|
|
||||||
supportMetrics: boolean;
|
|
||||||
url: string;
|
url: string;
|
||||||
directUrl: string;
|
directUrl: string;
|
||||||
basicAuth: any;
|
basicAuth: any;
|
||||||
@@ -166,8 +164,6 @@ export class PrometheusDatasource {
|
|||||||
this.type = 'prometheus';
|
this.type = 'prometheus';
|
||||||
this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
|
this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
|
||||||
this.name = instanceSettings.name;
|
this.name = instanceSettings.name;
|
||||||
this.supportsExplore = true;
|
|
||||||
this.supportMetrics = true;
|
|
||||||
this.url = instanceSettings.url;
|
this.url = instanceSettings.url;
|
||||||
this.directUrl = instanceSettings.directUrl;
|
this.directUrl = instanceSettings.directUrl;
|
||||||
this.basicAuth = instanceSettings.basicAuth;
|
this.basicAuth = instanceSettings.basicAuth;
|
||||||
@@ -522,10 +518,10 @@ export class PrometheusDatasource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getExploreState(panel) {
|
getExploreState(targets: any[]) {
|
||||||
let state = {};
|
let state = {};
|
||||||
if (panel.targets) {
|
if (targets && targets.length > 0) {
|
||||||
const queries = panel.targets.map(t => ({
|
const queries = targets.map(t => ({
|
||||||
query: this.templateSrv.replace(t.expr, {}, this.interpolateQueryExpr),
|
query: this.templateSrv.replace(t.expr, {}, this.interpolateQueryExpr),
|
||||||
format: t.format,
|
format: t.format,
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -9,6 +9,31 @@ export interface Query {
|
|||||||
key?: string;
|
key?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExploreState {
|
||||||
|
datasource: any;
|
||||||
|
datasourceError: any;
|
||||||
|
datasourceLoading: boolean | null;
|
||||||
|
datasourceMissing: boolean;
|
||||||
|
datasourceName?: string;
|
||||||
|
graphResult: any;
|
||||||
|
history: any[];
|
||||||
|
latency: number;
|
||||||
|
loading: any;
|
||||||
|
logsResult: any;
|
||||||
|
queries: Query[];
|
||||||
|
queryErrors: any[];
|
||||||
|
queryHints: any[];
|
||||||
|
range: Range;
|
||||||
|
requestOptions: any;
|
||||||
|
showingGraph: boolean;
|
||||||
|
showingLogs: boolean;
|
||||||
|
showingTable: boolean;
|
||||||
|
supportsGraph: boolean | null;
|
||||||
|
supportsLogs: boolean | null;
|
||||||
|
supportsTable: boolean | null;
|
||||||
|
tableResult: any;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ExploreUrlState {
|
export interface ExploreUrlState {
|
||||||
datasource: string;
|
datasource: string;
|
||||||
queries: Query[];
|
queries: Query[];
|
||||||
|
|||||||
Reference in New Issue
Block a user