mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -2,19 +2,20 @@ import React from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
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 colors from 'app/core/utils/colors';
|
||||
import store from 'app/core/store';
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
import { parse as parseDate } from 'app/core/utils/datemath';
|
||||
import { DEFAULT_RANGE } from 'app/core/utils/explore';
|
||||
|
||||
import ElapsedTime from './ElapsedTime';
|
||||
import QueryRows from './QueryRows';
|
||||
import Graph from './Graph';
|
||||
import Logs from './Logs';
|
||||
import Table from './Table';
|
||||
import TimePicker, { DEFAULT_RANGE } from './TimePicker';
|
||||
import TimePicker from './TimePicker';
|
||||
import { ensureQueries, generateQueryKey, hasQuery } from './utils/query';
|
||||
|
||||
const MAX_HISTORY_ITEMS = 100;
|
||||
@@ -58,31 +59,6 @@ interface ExploreProps {
|
||||
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> {
|
||||
el: any;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import * as dateMath from 'app/core/utils/datemath';
|
||||
import * as rangeUtil from 'app/core/utils/rangeutil';
|
||||
|
||||
const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
||||
|
||||
export const DEFAULT_RANGE = {
|
||||
from: 'now-6h',
|
||||
to: 'now',
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
import { serializeStateToUrlParam, parseUrlState } from './Wrapper';
|
||||
import { DEFAULT_RANGE } from './TimePicker';
|
||||
import { ExploreState } from './Explore';
|
||||
|
||||
const DEFAULT_EXPLORE_STATE: ExploreState = {
|
||||
datasource: null,
|
||||
datasourceError: null,
|
||||
datasourceLoading: null,
|
||||
datasourceMissing: false,
|
||||
datasourceName: '',
|
||||
graphResult: null,
|
||||
history: [],
|
||||
latency: 0,
|
||||
loading: false,
|
||||
logsResult: null,
|
||||
queries: [],
|
||||
queryErrors: [],
|
||||
queryHints: [],
|
||||
range: DEFAULT_RANGE,
|
||||
requestOptions: null,
|
||||
showingGraph: true,
|
||||
showingLogs: true,
|
||||
showingTable: true,
|
||||
supportsGraph: null,
|
||||
supportsLogs: null,
|
||||
supportsTable: null,
|
||||
tableResult: null,
|
||||
};
|
||||
|
||||
describe('Wrapper state functions', () => {
|
||||
describe('parseUrlState', () => {
|
||||
it('returns default state on empty string', () => {
|
||||
expect(parseUrlState('')).toMatchObject({
|
||||
datasource: null,
|
||||
queries: [],
|
||||
range: DEFAULT_RANGE,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('serializeStateToUrlParam', () => {
|
||||
it('returns url parameter value for a state object', () => {
|
||||
const state = {
|
||||
...DEFAULT_EXPLORE_STATE,
|
||||
datasourceName: 'foo',
|
||||
range: {
|
||||
from: 'now - 5h',
|
||||
to: 'now',
|
||||
},
|
||||
queries: [
|
||||
{
|
||||
query: 'metric{test="a/b"}',
|
||||
},
|
||||
{
|
||||
query: 'super{foo="x/z"}',
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(serializeStateToUrlParam(state)).toBe(
|
||||
'{"datasource":"foo","queries":[{"query":"metric{test=\\"a/b\\"}"},' +
|
||||
'{"query":"super{foo=\\"x/z\\"}"}],"range":{"from":"now - 5h","to":"now"}}'
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('interplay', () => {
|
||||
it('can parse the serialized state into the original state', () => {
|
||||
const state = {
|
||||
...DEFAULT_EXPLORE_STATE,
|
||||
datasourceName: 'foo',
|
||||
range: {
|
||||
from: 'now - 5h',
|
||||
to: 'now',
|
||||
},
|
||||
queries: [
|
||||
{
|
||||
query: 'metric{test="a/b"}',
|
||||
},
|
||||
{
|
||||
query: 'super{foo="x/z"}',
|
||||
},
|
||||
],
|
||||
};
|
||||
const serialized = serializeStateToUrlParam(state);
|
||||
const parsed = parseUrlState(serialized);
|
||||
|
||||
// Account for datasource vs datasourceName
|
||||
const { datasource, ...rest } = parsed;
|
||||
const sameState = {
|
||||
...rest,
|
||||
datasource: DEFAULT_EXPLORE_STATE.datasource,
|
||||
datasourceName: datasource,
|
||||
};
|
||||
|
||||
expect(state).toMatchObject(sameState);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,31 +3,11 @@ import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
import { serializeStateToUrlParam, parseUrlState } from 'app/core/utils/explore';
|
||||
import { StoreState } from 'app/types';
|
||||
import { ExploreUrlState } from 'app/types/explore';
|
||||
import { ExploreState } from 'app/types/explore';
|
||||
|
||||
import Explore, { ExploreState } 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);
|
||||
}
|
||||
import Explore from './Explore';
|
||||
|
||||
interface WrapperProps {
|
||||
backendSrv?: any;
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user