|
@@ -8,6 +8,7 @@ import Legend from './Legend';
|
|
|
import QueryRows from './QueryRows';
|
|
import QueryRows from './QueryRows';
|
|
|
import Graph from './Graph';
|
|
import Graph from './Graph';
|
|
|
import Table from './Table';
|
|
import Table from './Table';
|
|
|
|
|
+import TimePicker, { DEFAULT_RANGE } from './TimePicker';
|
|
|
import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
|
|
import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
|
|
|
import { buildQueryOptions, ensureQueries, generateQueryKey, hasQuery } from './utils/query';
|
|
import { buildQueryOptions, ensureQueries, generateQueryKey, hasQuery } from './utils/query';
|
|
|
import { decodePathComponent } from 'app/core/utils/location_util';
|
|
import { decodePathComponent } from 'app/core/utils/location_util';
|
|
@@ -15,40 +16,33 @@ import { decodePathComponent } from 'app/core/utils/location_util';
|
|
|
function makeTimeSeriesList(dataList, options) {
|
|
function makeTimeSeriesList(dataList, options) {
|
|
|
return dataList.map((seriesData, index) => {
|
|
return dataList.map((seriesData, index) => {
|
|
|
const datapoints = seriesData.datapoints || [];
|
|
const datapoints = seriesData.datapoints || [];
|
|
|
- const alias = seriesData.target;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const responseAlias = seriesData.target;
|
|
|
|
|
+ const query = options.targets[index].expr;
|
|
|
|
|
+ const alias = responseAlias && responseAlias !== '{}' ? responseAlias : query;
|
|
|
const colorIndex = index % colors.length;
|
|
const colorIndex = index % colors.length;
|
|
|
const color = colors[colorIndex];
|
|
const color = colors[colorIndex];
|
|
|
|
|
|
|
|
const series = new TimeSeries({
|
|
const series = new TimeSeries({
|
|
|
- datapoints: datapoints,
|
|
|
|
|
- alias: alias,
|
|
|
|
|
- color: color,
|
|
|
|
|
|
|
+ datapoints,
|
|
|
|
|
+ alias,
|
|
|
|
|
+ color,
|
|
|
unit: seriesData.unit,
|
|
unit: seriesData.unit,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- if (datapoints && datapoints.length > 0) {
|
|
|
|
|
- const last = datapoints[datapoints.length - 1][1];
|
|
|
|
|
- const from = options.range.from;
|
|
|
|
|
- if (last - from < -10000) {
|
|
|
|
|
- series.isOutsideRange = true;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
return series;
|
|
return series;
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function parseInitialQueries(initial) {
|
|
|
|
|
- if (!initial) {
|
|
|
|
|
- return [];
|
|
|
|
|
- }
|
|
|
|
|
|
|
+function parseInitialState(initial) {
|
|
|
try {
|
|
try {
|
|
|
const parsed = JSON.parse(decodePathComponent(initial));
|
|
const parsed = JSON.parse(decodePathComponent(initial));
|
|
|
- return parsed.queries.map(q => q.query);
|
|
|
|
|
|
|
+ return {
|
|
|
|
|
+ queries: parsed.queries.map(q => q.query),
|
|
|
|
|
+ range: parsed.range,
|
|
|
|
|
+ };
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
console.error(e);
|
|
console.error(e);
|
|
|
- return [];
|
|
|
|
|
|
|
+ return { queries: [], range: DEFAULT_RANGE };
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -60,6 +54,7 @@ interface IExploreState {
|
|
|
latency: number;
|
|
latency: number;
|
|
|
loading: any;
|
|
loading: any;
|
|
|
queries: any;
|
|
queries: any;
|
|
|
|
|
+ range: any;
|
|
|
requestOptions: any;
|
|
requestOptions: any;
|
|
|
showingGraph: boolean;
|
|
showingGraph: boolean;
|
|
|
showingTable: boolean;
|
|
showingTable: boolean;
|
|
@@ -72,7 +67,7 @@ export class Explore extends React.Component<any, IExploreState> {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
constructor(props) {
|
|
|
super(props);
|
|
super(props);
|
|
|
- const initialQueries = parseInitialQueries(props.routeParams.initial);
|
|
|
|
|
|
|
+ const { range, queries } = parseInitialState(props.routeParams.initial);
|
|
|
this.state = {
|
|
this.state = {
|
|
|
datasource: null,
|
|
datasource: null,
|
|
|
datasourceError: null,
|
|
datasourceError: null,
|
|
@@ -80,7 +75,8 @@ export class Explore extends React.Component<any, IExploreState> {
|
|
|
graphResult: null,
|
|
graphResult: null,
|
|
|
latency: 0,
|
|
latency: 0,
|
|
|
loading: false,
|
|
loading: false,
|
|
|
- queries: ensureQueries(initialQueries),
|
|
|
|
|
|
|
+ queries: ensureQueries(queries),
|
|
|
|
|
+ range: range || { ...DEFAULT_RANGE },
|
|
|
requestOptions: null,
|
|
requestOptions: null,
|
|
|
showingGraph: true,
|
|
showingGraph: true,
|
|
|
showingTable: true,
|
|
showingTable: true,
|
|
@@ -119,6 +115,14 @@ export class Explore extends React.Component<any, IExploreState> {
|
|
|
this.setState({ queries: nextQueries });
|
|
this.setState({ queries: nextQueries });
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ handleChangeTime = nextRange => {
|
|
|
|
|
+ const range = {
|
|
|
|
|
+ from: nextRange.from,
|
|
|
|
|
+ to: nextRange.to,
|
|
|
|
|
+ };
|
|
|
|
|
+ this.setState({ range }, () => this.handleSubmit());
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
handleClickGraphButton = () => {
|
|
handleClickGraphButton = () => {
|
|
|
this.setState(state => ({ showingGraph: !state.showingGraph }));
|
|
this.setState(state => ({ showingGraph: !state.showingGraph }));
|
|
|
};
|
|
};
|
|
@@ -147,7 +151,7 @@ export class Explore extends React.Component<any, IExploreState> {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
async runGraphQuery() {
|
|
async runGraphQuery() {
|
|
|
- const { datasource, queries } = this.state;
|
|
|
|
|
|
|
+ const { datasource, queries, range } = this.state;
|
|
|
if (!hasQuery(queries)) {
|
|
if (!hasQuery(queries)) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -157,7 +161,7 @@ export class Explore extends React.Component<any, IExploreState> {
|
|
|
format: 'time_series',
|
|
format: 'time_series',
|
|
|
interval: datasource.interval,
|
|
interval: datasource.interval,
|
|
|
instant: false,
|
|
instant: false,
|
|
|
- now,
|
|
|
|
|
|
|
+ range,
|
|
|
queries: queries.map(q => q.query),
|
|
queries: queries.map(q => q.query),
|
|
|
});
|
|
});
|
|
|
try {
|
|
try {
|
|
@@ -172,7 +176,7 @@ export class Explore extends React.Component<any, IExploreState> {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async runTableQuery() {
|
|
async runTableQuery() {
|
|
|
- const { datasource, queries } = this.state;
|
|
|
|
|
|
|
+ const { datasource, queries, range } = this.state;
|
|
|
if (!hasQuery(queries)) {
|
|
if (!hasQuery(queries)) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -182,7 +186,7 @@ export class Explore extends React.Component<any, IExploreState> {
|
|
|
format: 'table',
|
|
format: 'table',
|
|
|
interval: datasource.interval,
|
|
interval: datasource.interval,
|
|
|
instant: true,
|
|
instant: true,
|
|
|
- now,
|
|
|
|
|
|
|
+ range,
|
|
|
queries: queries.map(q => q.query),
|
|
queries: queries.map(q => q.query),
|
|
|
});
|
|
});
|
|
|
try {
|
|
try {
|
|
@@ -210,6 +214,7 @@ export class Explore extends React.Component<any, IExploreState> {
|
|
|
latency,
|
|
latency,
|
|
|
loading,
|
|
loading,
|
|
|
queries,
|
|
queries,
|
|
|
|
|
+ range,
|
|
|
requestOptions,
|
|
requestOptions,
|
|
|
showingGraph,
|
|
showingGraph,
|
|
|
showingTable,
|
|
showingTable,
|
|
@@ -229,14 +234,8 @@ export class Explore extends React.Component<any, IExploreState> {
|
|
|
|
|
|
|
|
{datasource ? (
|
|
{datasource ? (
|
|
|
<div className="m-r-3">
|
|
<div className="m-r-3">
|
|
|
- <div className="nav m-b-1">
|
|
|
|
|
- <div className="pull-right">
|
|
|
|
|
- {loading || latency ? <ElapsedTime time={latency} className="" /> : null}
|
|
|
|
|
- <button type="submit" className="m-l-1 btn btn-primary" onClick={this.handleSubmit}>
|
|
|
|
|
- <i className="fa fa-return" /> Run Query
|
|
|
|
|
- </button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div>
|
|
|
|
|
|
|
+ <div className="nav m-b-1 navbar">
|
|
|
|
|
+ <div className="navbar-buttons">
|
|
|
<button className={graphButtonClassName} onClick={this.handleClickGraphButton}>
|
|
<button className={graphButtonClassName} onClick={this.handleClickGraphButton}>
|
|
|
Graph
|
|
Graph
|
|
|
</button>
|
|
</button>
|
|
@@ -244,6 +243,14 @@ export class Explore extends React.Component<any, IExploreState> {
|
|
|
Table
|
|
Table
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div className="navbar__spacer" />
|
|
|
|
|
+ <TimePicker range={range} onChangeTime={this.handleChangeTime} />
|
|
|
|
|
+ <div className="navbar-buttons">
|
|
|
|
|
+ <button type="submit" className="btn btn-primary" onClick={this.handleSubmit}>
|
|
|
|
|
+ <i className="fa fa-return" /> Run Query
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {loading || latency ? <ElapsedTime time={latency} className="" /> : null}
|
|
|
</div>
|
|
</div>
|
|
|
<QueryRows
|
|
<QueryRows
|
|
|
queries={queries}
|
|
queries={queries}
|