Renamed targets to queries

This commit is contained in:
David Kaltschmidt
2018-11-21 16:28:30 +01:00
parent b3161bea5a
commit 22987ee75f
10 changed files with 168 additions and 168 deletions

View File

@@ -10,7 +10,7 @@ const DEFAULT_EXPLORE_STATE: ExploreState = {
exploreDatasources: [], exploreDatasources: [],
graphRange: DEFAULT_RANGE, graphRange: DEFAULT_RANGE,
history: [], history: [],
initialTargets: [], initialQueries: [],
queryTransactions: [], queryTransactions: [],
range: DEFAULT_RANGE, range: DEFAULT_RANGE,
showingGraph: true, showingGraph: true,
@@ -26,17 +26,17 @@ describe('state functions', () => {
it('returns default state on empty string', () => { it('returns default state on empty string', () => {
expect(parseUrlState('')).toMatchObject({ expect(parseUrlState('')).toMatchObject({
datasource: null, datasource: null,
targets: [], queries: [],
range: DEFAULT_RANGE, range: DEFAULT_RANGE,
}); });
}); });
it('returns a valid Explore state from URL parameter', () => { it('returns a valid Explore state from URL parameter', () => {
const paramValue = const paramValue =
'%7B"datasource":"Local","targets":%5B%7B"expr":"metric"%7D%5D,"range":%7B"from":"now-1h","to":"now"%7D%7D'; '%7B"datasource":"Local","queries":%5B%7B"expr":"metric"%7D%5D,"range":%7B"from":"now-1h","to":"now"%7D%7D';
expect(parseUrlState(paramValue)).toMatchObject({ expect(parseUrlState(paramValue)).toMatchObject({
datasource: 'Local', datasource: 'Local',
targets: [{ expr: 'metric' }], queries: [{ expr: 'metric' }],
range: { range: {
from: 'now-1h', from: 'now-1h',
to: 'now', to: 'now',
@@ -48,7 +48,7 @@ describe('state functions', () => {
const paramValue = '%5B"now-1h","now","Local",%7B"expr":"metric"%7D%5D'; const paramValue = '%5B"now-1h","now","Local",%7B"expr":"metric"%7D%5D';
expect(parseUrlState(paramValue)).toMatchObject({ expect(parseUrlState(paramValue)).toMatchObject({
datasource: 'Local', datasource: 'Local',
targets: [{ expr: 'metric' }], queries: [{ expr: 'metric' }],
range: { range: {
from: 'now-1h', from: 'now-1h',
to: 'now', to: 'now',
@@ -66,7 +66,7 @@ describe('state functions', () => {
from: 'now-5h', from: 'now-5h',
to: 'now', to: 'now',
}, },
initialTargets: [ initialQueries: [
{ {
refId: '1', refId: '1',
expr: 'metric{test="a/b"}', expr: 'metric{test="a/b"}',
@@ -78,7 +78,7 @@ describe('state functions', () => {
], ],
}; };
expect(serializeStateToUrlParam(state)).toBe( expect(serializeStateToUrlParam(state)).toBe(
'{"datasource":"foo","targets":[{"expr":"metric{test=\\"a/b\\"}"},' + '{"datasource":"foo","queries":[{"expr":"metric{test=\\"a/b\\"}"},' +
'{"expr":"super{foo=\\"x/z\\"}"}],"range":{"from":"now-5h","to":"now"}}' '{"expr":"super{foo=\\"x/z\\"}"}],"range":{"from":"now-5h","to":"now"}}'
); );
}); });
@@ -91,7 +91,7 @@ describe('state functions', () => {
from: 'now-5h', from: 'now-5h',
to: 'now', to: 'now',
}, },
initialTargets: [ initialQueries: [
{ {
refId: '1', refId: '1',
expr: 'metric{test="a/b"}', expr: 'metric{test="a/b"}',
@@ -117,7 +117,7 @@ describe('state functions', () => {
from: 'now - 5h', from: 'now - 5h',
to: 'now', to: 'now',
}, },
initialTargets: [ initialQueries: [
{ {
refId: '1', refId: '1',
expr: 'metric{test="a/b"}', expr: 'metric{test="a/b"}',
@@ -132,12 +132,12 @@ describe('state functions', () => {
const parsed = parseUrlState(serialized); const parsed = parseUrlState(serialized);
// Account for datasource vs datasourceName // Account for datasource vs datasourceName
const { datasource, targets, ...rest } = parsed; const { datasource, queries, ...rest } = parsed;
const resultState = { const resultState = {
...rest, ...rest,
datasource: DEFAULT_EXPLORE_STATE.datasource, datasource: DEFAULT_EXPLORE_STATE.datasource,
datasourceName: datasource, datasourceName: datasource,
initialTargets: targets, initialQueries: queries,
}; };
expect(state).toMatchObject(resultState); expect(state).toMatchObject(resultState);

View File

@@ -32,7 +32,7 @@ export async function getExploreUrl(
timeSrv: any timeSrv: any
) { ) {
let exploreDatasource = panelDatasource; let exploreDatasource = panelDatasource;
let exploreTargets = panelTargets; let exploreTargets: DataQuery[] = panelTargets;
let url; let url;
// Mixed datasources need to choose only one datasource // Mixed datasources need to choose only one datasource
@@ -79,25 +79,25 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState {
to: parsed[1], to: parsed[1],
}; };
const datasource = parsed[2]; const datasource = parsed[2];
const targets = parsed.slice(3); const queries = parsed.slice(3);
return { datasource, targets, range }; return { datasource, queries, range };
} }
return parsed; return parsed;
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
} }
return { datasource: null, targets: [], range: DEFAULT_RANGE }; return { datasource: null, queries: [], range: DEFAULT_RANGE };
} }
export function serializeStateToUrlParam(state: ExploreState, compact?: boolean): string { export function serializeStateToUrlParam(state: ExploreState, compact?: boolean): string {
const urlState: ExploreUrlState = { const urlState: ExploreUrlState = {
datasource: state.datasourceName, datasource: state.datasourceName,
targets: state.initialTargets.map(({ key, refId, ...rest }) => rest), queries: state.initialQueries.map(({ key, refId, ...rest }) => rest),
range: state.range, range: state.range,
}; };
if (compact) { if (compact) {
return JSON.stringify([urlState.range.from, urlState.range.to, urlState.datasource, ...urlState.targets]); return JSON.stringify([urlState.range.from, urlState.range.to, urlState.datasource, ...urlState.queries]);
} }
return JSON.stringify(urlState); return JSON.stringify(urlState);
} }
@@ -110,25 +110,25 @@ export function generateRefId(index = 0): string {
return `${index + 1}`; return `${index + 1}`;
} }
export function generateTargetKeys(index = 0): { refId: string; key: string } { export function generateQueryKeys(index = 0): { refId: string; key: string } {
return { refId: generateRefId(index), key: generateKey(index) }; return { refId: generateRefId(index), key: generateKey(index) };
} }
/** /**
* Ensure at least one target exists and that targets have the necessary keys * Ensure at least one target exists and that targets have the necessary keys
*/ */
export function ensureTargets(targets?: DataQuery[]): DataQuery[] { export function ensureQueries(queries?: DataQuery[]): DataQuery[] {
if (targets && typeof targets === 'object' && targets.length > 0) { if (queries && typeof queries === 'object' && queries.length > 0) {
return targets.map((target, i) => ({ ...target, ...generateTargetKeys(i) })); return queries.map((query, i) => ({ ...query, ...generateQueryKeys(i) }));
} }
return [{ ...generateTargetKeys() }]; return [{ ...generateQueryKeys() }];
} }
/** /**
* A target is non-empty when it has keys other than refId and key. * A target is non-empty when it has keys other than refId and key.
*/ */
export function hasNonEmptyTarget(targets: DataQuery[]): boolean { export function hasNonEmptyQuery(queries: DataQuery[]): boolean {
return targets.some(target => Object.keys(target).length > 2); return queries.some(query => Object.keys(query).length > 2);
} }
export function getIntervals( export function getIntervals(
@@ -146,7 +146,7 @@ export function getIntervals(
return kbn.calculateInterval(absoluteRange, resolution, datasource.interval); return kbn.calculateInterval(absoluteRange, resolution, datasource.interval);
} }
export function makeTimeSeriesList(dataList, options) { export function makeTimeSeriesList(dataList) {
return dataList.map((seriesData, index) => { return dataList.map((seriesData, index) => {
const datapoints = seriesData.datapoints || []; const datapoints = seriesData.datapoints || [];
const alias = seriesData.target; const alias = seriesData.target;
@@ -167,10 +167,10 @@ export function makeTimeSeriesList(dataList, options) {
/** /**
* Update the query history. Side-effect: store history in local storage * Update the query history. Side-effect: store history in local storage
*/ */
export function updateHistory(history: HistoryItem[], datasourceId: string, targets: DataQuery[]): HistoryItem[] { export function updateHistory(history: HistoryItem[], datasourceId: string, queries: DataQuery[]): HistoryItem[] {
const ts = Date.now(); const ts = Date.now();
targets.forEach(target => { queries.forEach(query => {
history = [{ target, ts }, ...history]; history = [{ query, ts }, ...history];
}); });
if (history.length > MAX_HISTORY_ITEMS) { if (history.length > MAX_HISTORY_ITEMS) {

View File

@@ -16,11 +16,11 @@ import { RawTimeRange, DataQuery } from 'app/types/series';
import store from 'app/core/store'; import store from 'app/core/store';
import { import {
DEFAULT_RANGE, DEFAULT_RANGE,
ensureTargets, ensureQueries,
getIntervals, getIntervals,
generateKey, generateKey,
generateTargetKeys, generateQueryKeys,
hasNonEmptyTarget, hasNonEmptyQuery,
makeTimeSeriesList, makeTimeSeriesList,
updateHistory, updateHistory,
} from 'app/core/utils/explore'; } from 'app/core/utils/explore';
@@ -63,7 +63,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
* Current query expressions of the rows including their modifications, used for running queries. * Current query expressions of the rows including their modifications, used for running queries.
* Not kept in component state to prevent edit-render roundtrips. * Not kept in component state to prevent edit-render roundtrips.
*/ */
modifiedTargets: DataQuery[]; modifiedQueries: DataQuery[];
/** /**
* Local ID cache to compare requested vs selected datasource * Local ID cache to compare requested vs selected datasource
*/ */
@@ -72,14 +72,14 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
constructor(props) { constructor(props) {
super(props); super(props);
const splitState: ExploreState = props.splitState; const splitState: ExploreState = props.splitState;
let initialTargets: DataQuery[]; let initialQueries: DataQuery[];
if (splitState) { if (splitState) {
// Split state overrides everything // Split state overrides everything
this.state = splitState; this.state = splitState;
initialTargets = splitState.initialTargets; initialQueries = splitState.initialQueries;
} else { } else {
const { datasource, targets, range } = props.urlState as ExploreUrlState; const { datasource, queries, range } = props.urlState as ExploreUrlState;
initialTargets = ensureTargets(targets); initialQueries = ensureQueries(queries);
const initialRange = range || { ...DEFAULT_RANGE }; const initialRange = range || { ...DEFAULT_RANGE };
this.state = { this.state = {
datasource: null, datasource: null,
@@ -89,7 +89,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
datasourceName: datasource, datasourceName: datasource,
exploreDatasources: [], exploreDatasources: [],
graphRange: initialRange, graphRange: initialRange,
initialTargets, initialQueries,
history: [], history: [],
queryTransactions: [], queryTransactions: [],
range: initialRange, range: initialRange,
@@ -102,7 +102,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
supportsTable: null, supportsTable: null,
}; };
} }
this.modifiedTargets = initialTargets.slice(); this.modifiedQueries = initialQueries.slice();
} }
async componentDidMount() { async componentDidMount() {
@@ -165,26 +165,26 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
} }
// Check if queries can be imported from previously selected datasource // Check if queries can be imported from previously selected datasource
let modifiedTargets = this.modifiedTargets; let modifiedQueries = this.modifiedQueries;
if (origin) { if (origin) {
if (origin.meta.id === datasource.meta.id) { if (origin.meta.id === datasource.meta.id) {
// Keep same queries if same type of datasource // Keep same queries if same type of datasource
modifiedTargets = [...this.modifiedTargets]; modifiedQueries = [...this.modifiedQueries];
} else if (datasource.importQueries) { } else if (datasource.importQueries) {
// Datasource-specific importers // Datasource-specific importers
modifiedTargets = await datasource.importQueries(this.modifiedTargets, origin.meta); modifiedQueries = await datasource.importQueries(this.modifiedQueries, origin.meta);
} else { } else {
// Default is blank queries // Default is blank queries
modifiedTargets = ensureTargets(); modifiedQueries = ensureQueries();
} }
} }
// Reset edit state with new queries // Reset edit state with new queries
const nextTargets = this.state.initialTargets.map((q, i) => ({ const nextQueries = this.state.initialQueries.map((q, i) => ({
...modifiedTargets[i], ...modifiedQueries[i],
...generateTargetKeys(i), ...generateQueryKeys(i),
})); }));
this.modifiedTargets = modifiedTargets; this.modifiedQueries = modifiedQueries;
// Custom components // Custom components
const StartPage = datasource.pluginExports.ExploreStartPage; const StartPage = datasource.pluginExports.ExploreStartPage;
@@ -200,7 +200,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
supportsTable, supportsTable,
datasourceLoading: false, datasourceLoading: false,
datasourceName: datasource.name, datasourceName: datasource.name,
initialTargets: nextTargets, initialQueries: nextQueries,
showingStartPage: Boolean(StartPage), showingStartPage: Boolean(StartPage),
}, },
() => { () => {
@@ -217,15 +217,15 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
onAddQueryRow = index => { onAddQueryRow = index => {
// Local cache // Local cache
this.modifiedTargets[index + 1] = { ...generateTargetKeys(index + 1) }; this.modifiedQueries[index + 1] = { ...generateQueryKeys(index + 1) };
this.setState(state => { this.setState(state => {
const { initialTargets, queryTransactions } = state; const { initialQueries, queryTransactions } = state;
const nextTargets = [ const nextQueries = [
...initialTargets.slice(0, index + 1), ...initialQueries.slice(0, index + 1),
{ ...this.modifiedTargets[index + 1] }, { ...this.modifiedQueries[index + 1] },
...initialTargets.slice(index + 1), ...initialQueries.slice(index + 1),
]; ];
// Ongoing transactions need to update their row indices // Ongoing transactions need to update their row indices
@@ -239,7 +239,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
return qt; return qt;
}); });
return { initialTargets: nextTargets, queryTransactions: nextQueryTransactions }; return { initialQueries: nextQueries, queryTransactions: nextQueryTransactions };
}); });
}; };
@@ -258,24 +258,24 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
onChangeQuery = (value: DataQuery, index: number, override?: boolean) => { onChangeQuery = (value: DataQuery, index: number, override?: boolean) => {
// Keep current value in local cache // Keep current value in local cache
this.modifiedTargets[index] = value; this.modifiedQueries[index] = value;
if (override) { if (override) {
this.setState(state => { this.setState(state => {
// Replace query row // Replace query row
const { initialTargets, queryTransactions } = state; const { initialQueries, queryTransactions } = state;
const target: DataQuery = { const query: DataQuery = {
...value, ...value,
...generateTargetKeys(index), ...generateQueryKeys(index),
}; };
const nextTargets = [...initialTargets]; const nextQueries = [...initialQueries];
nextTargets[index] = target; nextQueries[index] = query;
// Discard ongoing transaction related to row query // Discard ongoing transaction related to row query
const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index); const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
return { return {
initialTargets: nextTargets, initialQueries: nextQueries,
queryTransactions: nextQueryTransactions, queryTransactions: nextQueryTransactions,
}; };
}, this.onSubmit); }, this.onSubmit);
@@ -290,10 +290,10 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
}; };
onClickClear = () => { onClickClear = () => {
this.modifiedTargets = ensureTargets(); this.modifiedQueries = ensureQueries();
this.setState( this.setState(
prevState => ({ prevState => ({
initialTargets: [...this.modifiedTargets], initialQueries: [...this.modifiedQueries],
queryTransactions: [], queryTransactions: [],
showingStartPage: Boolean(prevState.StartPage), showingStartPage: Boolean(prevState.StartPage),
}), }),
@@ -347,10 +347,10 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
}; };
// Use this in help pages to set page to a single query // Use this in help pages to set page to a single query
onClickExample = (target: DataQuery) => { onClickExample = (query: DataQuery) => {
const nextTargets = [{ ...target, ...generateTargetKeys() }]; const nextQueries = [{ ...query, ...generateQueryKeys() }];
this.modifiedTargets = [...nextTargets]; this.modifiedQueries = [...nextQueries];
this.setState({ initialTargets: nextTargets }, this.onSubmit); this.setState({ initialQueries: nextQueries }, this.onSubmit);
}; };
onClickSplit = () => { onClickSplit = () => {
@@ -390,28 +390,28 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
const preventSubmit = action.preventSubmit; const preventSubmit = action.preventSubmit;
this.setState( this.setState(
state => { state => {
const { initialTargets, queryTransactions } = state; const { initialQueries, queryTransactions } = state;
let nextTargets: DataQuery[]; let nextQueries: DataQuery[];
let nextQueryTransactions; let nextQueryTransactions;
if (index === undefined) { if (index === undefined) {
// Modify all queries // Modify all queries
nextTargets = initialTargets.map((target, i) => ({ nextQueries = initialQueries.map((query, i) => ({
...datasource.modifyQuery(this.modifiedTargets[i], action), ...datasource.modifyQuery(this.modifiedQueries[i], action),
...generateTargetKeys(i), ...generateQueryKeys(i),
})); }));
// Discard all ongoing transactions // Discard all ongoing transactions
nextQueryTransactions = []; nextQueryTransactions = [];
} else { } else {
// Modify query only at index // Modify query only at index
nextTargets = initialTargets.map((target, i) => { nextQueries = initialQueries.map((query, i) => {
// Synchronise all queries with local query cache to ensure consistency // Synchronise all queries with local query cache to ensure consistency
// TODO still needed? // TODO still needed?
return i === index return i === index
? { ? {
...datasource.modifyQuery(this.modifiedTargets[i], action), ...datasource.modifyQuery(this.modifiedQueries[i], action),
...generateTargetKeys(i), ...generateQueryKeys(i),
} }
: target; : query;
}); });
nextQueryTransactions = queryTransactions nextQueryTransactions = queryTransactions
// Consume the hint corresponding to the action // Consume the hint corresponding to the action
@@ -424,9 +424,9 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
// Preserve previous row query transaction to keep results visible if next query is incomplete // Preserve previous row query transaction to keep results visible if next query is incomplete
.filter(qt => preventSubmit || qt.rowIndex !== index); .filter(qt => preventSubmit || qt.rowIndex !== index);
} }
this.modifiedTargets = [...nextTargets]; this.modifiedQueries = [...nextQueries];
return { return {
initialTargets: nextTargets, initialQueries: nextQueries,
queryTransactions: nextQueryTransactions, queryTransactions: nextQueryTransactions,
}; };
}, },
@@ -438,22 +438,22 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
onRemoveQueryRow = index => { onRemoveQueryRow = index => {
// Remove from local cache // Remove from local cache
this.modifiedTargets = [...this.modifiedTargets.slice(0, index), ...this.modifiedTargets.slice(index + 1)]; this.modifiedQueries = [...this.modifiedQueries.slice(0, index), ...this.modifiedQueries.slice(index + 1)];
this.setState( this.setState(
state => { state => {
const { initialTargets, queryTransactions } = state; const { initialQueries, queryTransactions } = state;
if (initialTargets.length <= 1) { if (initialQueries.length <= 1) {
return null; return null;
} }
// Remove row from react state // Remove row from react state
const nextTargets = [...initialTargets.slice(0, index), ...initialTargets.slice(index + 1)]; const nextQueries = [...initialQueries.slice(0, index), ...initialQueries.slice(index + 1)];
// Discard transactions related to row query // Discard transactions related to row query
const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index); const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
return { return {
initialTargets: nextTargets, initialQueries: nextQueries,
queryTransactions: nextQueryTransactions, queryTransactions: nextQueryTransactions,
}; };
}, },
@@ -475,36 +475,36 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
this.saveState(); this.saveState();
}; };
buildQueryOptions(target: DataQuery, targetOptions: { format: string; hinting?: boolean; instant?: boolean }) { buildQueryOptions(query: DataQuery, queryOptions: { format: string; hinting?: boolean; instant?: boolean }) {
const { datasource, range } = this.state; const { datasource, range } = this.state;
const { interval, intervalMs } = getIntervals(range, datasource, this.el.offsetWidth); const { interval, intervalMs } = getIntervals(range, datasource, this.el.offsetWidth);
const targets = [ const queries = [
{ {
...targetOptions, ...queryOptions,
...target, ...query,
}, },
]; ];
// Clone range for query request // Clone range for query request
const queryRange: RawTimeRange = { ...range }; const queryRange: RawTimeRange = { ...range };
// Datasource is using `panelId + target.refId` for cancellation logic. // Datasource is using `panelId + query.refId` for cancellation logic.
// Using `format` here because it relates to the view panel that the request is for. // Using `format` here because it relates to the view panel that the request is for.
const panelId = targetOptions.format; const panelId = queryOptions.format;
return { return {
interval, interval,
intervalMs, intervalMs,
panelId, panelId,
targets, queries,
range: queryRange, range: queryRange,
}; };
} }
startQueryTransaction(target: DataQuery, rowIndex: number, resultType: ResultType, options: any): QueryTransaction { startQueryTransaction(query: DataQuery, rowIndex: number, resultType: ResultType, options: any): QueryTransaction {
const queryOptions = this.buildQueryOptions(target, options); const queryOptions = this.buildQueryOptions(query, options);
const transaction: QueryTransaction = { const transaction: QueryTransaction = {
target, query,
resultType, resultType,
rowIndex, rowIndex,
id: generateKey(), // reusing for unique ID id: generateKey(), // reusing for unique ID
@@ -537,7 +537,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
transactionId: string, transactionId: string,
result: any, result: any,
latency: number, latency: number,
targets: DataQuery[], queries: DataQuery[],
datasourceId: string datasourceId: string
) { ) {
const { datasource } = this.state; const { datasource } = this.state;
@@ -558,7 +558,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
// Get query hints // Get query hints
let hints: QueryHint[]; let hints: QueryHint[];
if (datasource.getQueryHints as QueryHintGetter) { if (datasource.getQueryHints as QueryHintGetter) {
hints = datasource.getQueryHints(transaction.target, result); hints = datasource.getQueryHints(transaction.query, result);
} }
// Mark transactions as complete // Mark transactions as complete
@@ -575,7 +575,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
return qt; return qt;
}); });
const nextHistory = updateHistory(history, datasourceId, targets); const nextHistory = updateHistory(history, datasourceId, queries);
return { return {
history: nextHistory, history: nextHistory,
@@ -638,15 +638,15 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
} }
async runGraphQueries() { async runGraphQueries() {
const targets = [...this.modifiedTargets]; const queries = [...this.modifiedQueries];
if (!hasNonEmptyTarget(targets)) { if (!hasNonEmptyQuery(queries)) {
return; return;
} }
const { datasource } = this.state; const { datasource } = this.state;
const datasourceId = datasource.meta.id; const datasourceId = datasource.meta.id;
// Run all queries concurrently // Run all queries concurrently
targets.forEach(async (target, rowIndex) => { queries.forEach(async (query, rowIndex) => {
const transaction = this.startQueryTransaction(target, rowIndex, 'Graph', { const transaction = this.startQueryTransaction(query, rowIndex, 'Graph', {
format: 'time_series', format: 'time_series',
instant: false, instant: false,
}); });
@@ -654,8 +654,8 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
const now = Date.now(); const now = Date.now();
const res = await datasource.query(transaction.options); const res = await datasource.query(transaction.options);
const latency = Date.now() - now; const latency = Date.now() - now;
const results = makeTimeSeriesList(res.data, transaction.options); const results = makeTimeSeriesList(res.data);
this.completeQueryTransaction(transaction.id, results, latency, targets, datasourceId); this.completeQueryTransaction(transaction.id, results, latency, queries, datasourceId);
this.setState({ graphRange: transaction.options.range }); this.setState({ graphRange: transaction.options.range });
} catch (response) { } catch (response) {
this.failQueryTransaction(transaction.id, response, datasourceId); this.failQueryTransaction(transaction.id, response, datasourceId);
@@ -664,15 +664,15 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
} }
async runTableQuery() { async runTableQuery() {
const targets = [...this.modifiedTargets]; const queries = [...this.modifiedQueries];
if (!hasNonEmptyTarget(targets)) { if (!hasNonEmptyQuery(queries)) {
return; return;
} }
const { datasource } = this.state; const { datasource } = this.state;
const datasourceId = datasource.meta.id; const datasourceId = datasource.meta.id;
// Run all queries concurrently // Run all queries concurrently
targets.forEach(async (target, rowIndex) => { queries.forEach(async (query, rowIndex) => {
const transaction = this.startQueryTransaction(target, rowIndex, 'Table', { const transaction = this.startQueryTransaction(query, rowIndex, 'Table', {
format: 'table', format: 'table',
instant: true, instant: true,
valueWithRefId: true, valueWithRefId: true,
@@ -682,7 +682,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
const res = await datasource.query(transaction.options); const res = await datasource.query(transaction.options);
const latency = Date.now() - now; const latency = Date.now() - now;
const results = res.data[0]; const results = res.data[0];
this.completeQueryTransaction(transaction.id, results, latency, targets, datasourceId); this.completeQueryTransaction(transaction.id, results, latency, queries, datasourceId);
} catch (response) { } catch (response) {
this.failQueryTransaction(transaction.id, response, datasourceId); this.failQueryTransaction(transaction.id, response, datasourceId);
} }
@@ -690,21 +690,21 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
} }
async runLogsQuery() { async runLogsQuery() {
const targets = [...this.modifiedTargets]; const queries = [...this.modifiedQueries];
if (!hasNonEmptyTarget(targets)) { if (!hasNonEmptyQuery(queries)) {
return; return;
} }
const { datasource } = this.state; const { datasource } = this.state;
const datasourceId = datasource.meta.id; const datasourceId = datasource.meta.id;
// Run all queries concurrently // Run all queries concurrently
targets.forEach(async (target, rowIndex) => { queries.forEach(async (query, rowIndex) => {
const transaction = this.startQueryTransaction(target, rowIndex, 'Logs', { format: 'logs' }); const transaction = this.startQueryTransaction(query, rowIndex, 'Logs', { format: 'logs' });
try { try {
const now = Date.now(); const now = Date.now();
const res = await datasource.query(transaction.options); const res = await datasource.query(transaction.options);
const latency = Date.now() - now; const latency = Date.now() - now;
const results = res.data; const results = res.data;
this.completeQueryTransaction(transaction.id, results, latency, targets, datasourceId); this.completeQueryTransaction(transaction.id, results, latency, queries, datasourceId);
} catch (response) { } catch (response) {
this.failQueryTransaction(transaction.id, response, datasourceId); this.failQueryTransaction(transaction.id, response, datasourceId);
} }
@@ -716,7 +716,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
return { return {
...this.state, ...this.state,
queryTransactions: [], queryTransactions: [],
initialTargets: [...this.modifiedTargets], initialQueries: [...this.modifiedQueries],
}; };
} }
@@ -736,7 +736,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
exploreDatasources, exploreDatasources,
graphRange, graphRange,
history, history,
initialTargets, initialQueries,
queryTransactions, queryTransactions,
range, range,
showingGraph, showingGraph,
@@ -850,7 +850,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
<QueryRows <QueryRows
datasource={datasource} datasource={datasource}
history={history} history={history}
initialTargets={initialTargets} initialQueries={initialQueries}
onAddQueryRow={this.onAddQueryRow} onAddQueryRow={this.onAddQueryRow}
onChangeQuery={this.onChangeQuery} onChangeQuery={this.onChangeQuery}
onClickHintFix={this.onModifyQueries} onClickHintFix={this.onModifyQueries}

View File

@@ -32,7 +32,7 @@ interface QueryRowCommonProps {
type QueryRowProps = QueryRowCommonProps & type QueryRowProps = QueryRowCommonProps &
QueryRowEventHandlers & { QueryRowEventHandlers & {
index: number; index: number;
initialTarget: DataQuery; initialQuery: DataQuery;
}; };
class QueryRow extends PureComponent<QueryRowProps> { class QueryRow extends PureComponent<QueryRowProps> {
@@ -76,7 +76,7 @@ class QueryRow extends PureComponent<QueryRowProps> {
}; };
render() { render() {
const { datasource, history, initialTarget, transactions } = this.props; const { datasource, history, initialQuery, transactions } = this.props;
const transactionWithError = transactions.find(t => t.error !== undefined); const transactionWithError = transactions.find(t => t.error !== undefined);
const hint = getFirstHintFromTransactions(transactions); const hint = getFirstHintFromTransactions(transactions);
const queryError = transactionWithError ? transactionWithError.error : null; const queryError = transactionWithError ? transactionWithError.error : null;
@@ -91,7 +91,7 @@ class QueryRow extends PureComponent<QueryRowProps> {
datasource={datasource} datasource={datasource}
error={queryError} error={queryError}
hint={hint} hint={hint}
initialTarget={initialTarget} initialQuery={initialQuery}
history={history} history={history}
onClickHintFix={this.onClickHintFix} onClickHintFix={this.onClickHintFix}
onPressEnter={this.onPressEnter} onPressEnter={this.onPressEnter}
@@ -116,19 +116,19 @@ class QueryRow extends PureComponent<QueryRowProps> {
type QueryRowsProps = QueryRowCommonProps & type QueryRowsProps = QueryRowCommonProps &
QueryRowEventHandlers & { QueryRowEventHandlers & {
initialTargets: DataQuery[]; initialQueries: DataQuery[];
}; };
export default class QueryRows extends PureComponent<QueryRowsProps> { export default class QueryRows extends PureComponent<QueryRowsProps> {
render() { render() {
const { className = '', initialTargets, transactions, ...handlers } = this.props; const { className = '', initialQueries, transactions, ...handlers } = this.props;
return ( return (
<div className={className}> <div className={className}>
{initialTargets.map((target, index) => ( {initialQueries.map((query, index) => (
<QueryRow <QueryRow
key={target.key} key={query.key}
index={index} index={index}
initialTarget={target} initialQuery={query}
transactions={transactions.filter(t => t.rowIndex === index)} transactions={transactions.filter(t => t.rowIndex === index)}
{...handlers} {...handlers}
/> />

View File

@@ -54,7 +54,7 @@ interface LoggingQueryFieldProps {
error?: string | JSX.Element; error?: string | JSX.Element;
hint?: any; hint?: any;
history?: any[]; history?: any[];
initialTarget?: DataQuery; initialQuery?: DataQuery;
onClickHintFix?: (action: any) => void; onClickHintFix?: (action: any) => void;
onPressEnter?: () => void; onPressEnter?: () => void;
onQueryChange?: (value: DataQuery, override?: boolean) => void; onQueryChange?: (value: DataQuery, override?: boolean) => void;
@@ -135,13 +135,13 @@ class LoggingQueryField extends React.PureComponent<LoggingQueryFieldProps, Logg
onChangeQuery = (value: string, override?: boolean) => { onChangeQuery = (value: string, override?: boolean) => {
// Send text change to parent // Send text change to parent
const { initialTarget, onQueryChange } = this.props; const { initialQuery, onQueryChange } = this.props;
if (onQueryChange) { if (onQueryChange) {
const target = { const query = {
...initialTarget, ...initialQuery,
expr: value, expr: value,
}; };
onQueryChange(target, override); onQueryChange(query, override);
} }
}; };
@@ -186,7 +186,7 @@ class LoggingQueryField extends React.PureComponent<LoggingQueryFieldProps, Logg
}; };
render() { render() {
const { error, hint, initialTarget } = this.props; const { error, hint, initialQuery } = this.props;
const { logLabelOptions, syntaxLoaded } = this.state; const { logLabelOptions, syntaxLoaded } = this.state;
const cleanText = this.languageProvider ? this.languageProvider.cleanText : undefined; const cleanText = this.languageProvider ? this.languageProvider.cleanText : undefined;
const chooserText = syntaxLoaded ? 'Log labels' : 'Loading labels...'; const chooserText = syntaxLoaded ? 'Log labels' : 'Loading labels...';
@@ -204,7 +204,7 @@ class LoggingQueryField extends React.PureComponent<LoggingQueryFieldProps, Logg
<QueryField <QueryField
additionalPlugins={this.plugins} additionalPlugins={this.plugins}
cleanText={cleanText} cleanText={cleanText}
initialQuery={initialTarget.expr} initialQuery={initialQuery.expr}
onTypeahead={this.onTypeahead} onTypeahead={this.onTypeahead}
onWillApplySuggestion={willApplySuggestion} onWillApplySuggestion={willApplySuggestion}
onValueChanged={this.onChangeQuery} onValueChanged={this.onChangeQuery}

View File

@@ -112,8 +112,8 @@ export default class LoggingDatasource {
}); });
} }
async importQueries(targets: DataQuery[], originMeta: PluginMeta): Promise<DataQuery[]> { async importQueries(queries: DataQuery[], originMeta: PluginMeta): Promise<DataQuery[]> {
return this.languageProvider.importQueries(targets, originMeta.id); return this.languageProvider.importQueries(queries, originMeta.id);
} }
metadataRequest(url) { metadataRequest(url) {

View File

@@ -158,20 +158,20 @@ export default class LoggingLanguageProvider extends LanguageProvider {
return { context, refresher, suggestions }; return { context, refresher, suggestions };
} }
async importQueries(targets: DataQuery[], datasourceType: string): Promise<DataQuery[]> { async importQueries(queries: DataQuery[], datasourceType: string): Promise<DataQuery[]> {
if (datasourceType === 'prometheus') { if (datasourceType === 'prometheus') {
return Promise.all( return Promise.all(
targets.map(async target => { queries.map(async query => {
const expr = await this.importPrometheusQuery(target.expr); const expr = await this.importPrometheusQuery(query.expr);
return { return {
...target, ...query,
expr, expr,
}; };
}) })
); );
} }
return targets.map(target => ({ return queries.map(query => ({
...target, ...query,
expr: '', expr: '',
})); }));
} }

View File

@@ -88,7 +88,7 @@ interface CascaderOption {
type PromQueryFieldProps = { type PromQueryFieldProps = {
datasource: any; datasource: any;
error?: string | JSX.Element; error?: string | JSX.Element;
initialTarget: DataQuery; initialQuery: DataQuery;
hint?: any; hint?: any;
history?: any[]; history?: any[];
metricsByPrefix?: CascaderOption[]; metricsByPrefix?: CascaderOption[];
@@ -162,15 +162,15 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
this.onChangeQuery(query, true); this.onChangeQuery(query, true);
}; };
onChangeQuery = (query: string, override?: boolean) => { onChangeQuery = (value: string, override?: boolean) => {
// Send text change to parent // Send text change to parent
const { initialTarget, onQueryChange } = this.props; const { initialQuery, onQueryChange } = this.props;
if (onQueryChange) { if (onQueryChange) {
const target: DataQuery = { const query: DataQuery = {
...initialTarget, ...initialQuery,
expr: query, expr: value,
}; };
onQueryChange(target, override); onQueryChange(query, override);
} }
}; };
@@ -232,7 +232,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
}; };
render() { render() {
const { error, hint, initialTarget } = this.props; const { error, hint, initialQuery } = this.props;
const { metricsOptions, syntaxLoaded } = this.state; const { metricsOptions, syntaxLoaded } = this.state;
const cleanText = this.languageProvider ? this.languageProvider.cleanText : undefined; const cleanText = this.languageProvider ? this.languageProvider.cleanText : undefined;
const chooserText = syntaxLoaded ? 'Metrics' : 'Loading metrics...'; const chooserText = syntaxLoaded ? 'Metrics' : 'Loading metrics...';
@@ -250,7 +250,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
<QueryField <QueryField
additionalPlugins={this.plugins} additionalPlugins={this.plugins}
cleanText={cleanText} cleanText={cleanText}
initialQuery={initialTarget.expr} initialQuery={initialQuery.expr}
onTypeahead={this.onTypeahead} onTypeahead={this.onTypeahead}
onWillApplySuggestion={willApplySuggestion} onWillApplySuggestion={willApplySuggestion}
onValueChanged={this.onChangeQuery} onValueChanged={this.onChangeQuery}

View File

@@ -421,23 +421,23 @@ export class PrometheusDatasource {
}); });
} }
getExploreState(targets: DataQuery[]): Partial<ExploreUrlState> { getExploreState(queries: DataQuery[]): Partial<ExploreUrlState> {
let state: Partial<ExploreUrlState> = { datasource: this.name }; let state: Partial<ExploreUrlState> = { datasource: this.name };
if (targets && targets.length > 0) { if (queries && queries.length > 0) {
const expandedTargets = targets.map(target => ({ const expandedQueries = queries.map(query => ({
...target, ...query,
expr: this.templateSrv.replace(target.expr, {}, this.interpolateQueryExpr), expr: this.templateSrv.replace(query.expr, {}, this.interpolateQueryExpr),
})); }));
state = { state = {
...state, ...state,
targets: expandedTargets, queries: expandedQueries,
}; };
} }
return state; return state;
} }
getQueryHints(target: DataQuery, result: any[]) { getQueryHints(query: DataQuery, result: any[]) {
return getQueryHints(target.expr, result, this); return getQueryHints(query.expr, result, this);
} }
loadRules() { loadRules() {
@@ -455,35 +455,35 @@ export class PrometheusDatasource {
}); });
} }
modifyQuery(target: DataQuery, action: any): DataQuery { modifyQuery(query: DataQuery, action: any): DataQuery {
let query = target.expr; let expression = query.expr || '';
switch (action.type) { switch (action.type) {
case 'ADD_FILTER': { case 'ADD_FILTER': {
query = addLabelToQuery(query, action.key, action.value); expression = addLabelToQuery(expression, action.key, action.value);
break; break;
} }
case 'ADD_HISTOGRAM_QUANTILE': { case 'ADD_HISTOGRAM_QUANTILE': {
query = `histogram_quantile(0.95, sum(rate(${query}[5m])) by (le))`; expression = `histogram_quantile(0.95, sum(rate(${expression}[5m])) by (le))`;
break; break;
} }
case 'ADD_RATE': { case 'ADD_RATE': {
query = `rate(${query}[5m])`; expression = `rate(${expression}[5m])`;
break; break;
} }
case 'ADD_SUM': { case 'ADD_SUM': {
query = `sum(${query.trim()}) by ($1)`; expression = `sum(${expression.trim()}) by ($1)`;
break; break;
} }
case 'EXPAND_RULES': { case 'EXPAND_RULES': {
if (action.mapping) { if (action.mapping) {
query = expandRecordingRules(query, action.mapping); expression = expandRecordingRules(expression, action.mapping);
} }
break; break;
} }
default: default:
break; break;
} }
return { ...target, expr: query }; return { ...query, expr: expression };
} }
getPrometheusTime(date, roundUp) { getPrometheusTime(date, roundUp) {

View File

@@ -79,7 +79,7 @@ interface ExploreDatasource {
export interface HistoryItem { export interface HistoryItem {
ts: number; ts: number;
target: DataQuery; query: DataQuery;
} }
export abstract class LanguageProvider { export abstract class LanguageProvider {
@@ -126,7 +126,7 @@ export interface QueryHint {
} }
export interface QueryHintGetter { export interface QueryHintGetter {
(target: DataQuery, results: any[], ...rest: any): QueryHint[]; (query: DataQuery, results: any[], ...rest: any): QueryHint[];
} }
export interface QueryTransaction { export interface QueryTransaction {
@@ -136,10 +136,10 @@ export interface QueryTransaction {
hints?: QueryHint[]; hints?: QueryHint[];
latency: number; latency: number;
options: any; options: any;
query: DataQuery;
result?: any; // Table model / Timeseries[] / Logs result?: any; // Table model / Timeseries[] / Logs
resultType: ResultType; resultType: ResultType;
rowIndex: number; rowIndex: number;
target: DataQuery;
} }
export interface TextMatch { export interface TextMatch {
@@ -159,7 +159,7 @@ export interface ExploreState {
exploreDatasources: ExploreDatasource[]; exploreDatasources: ExploreDatasource[];
graphRange: RawTimeRange; graphRange: RawTimeRange;
history: HistoryItem[]; history: HistoryItem[];
initialTargets: DataQuery[]; initialQueries: DataQuery[];
queryTransactions: QueryTransaction[]; queryTransactions: QueryTransaction[];
range: RawTimeRange; range: RawTimeRange;
showingGraph: boolean; showingGraph: boolean;
@@ -173,7 +173,7 @@ export interface ExploreState {
export interface ExploreUrlState { export interface ExploreUrlState {
datasource: string; datasource: string;
targets: any[]; // Should be a DataQuery, but we're going to strip refIds, so typing makes less sense queries: any[]; // Should be a DataQuery, but we're going to strip refIds, so typing makes less sense
range: RawTimeRange; range: RawTimeRange;
} }