mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Refactor: improvements to PanelQueryRunner (#16678)
merged DataQueryOptions + DataRequestInfo info: DataQueryRequest
This commit is contained in:
parent
5f474c6328
commit
4e54509dde
@ -13,15 +13,23 @@ export enum FieldType {
|
||||
other = 'other', // Object, Array, etc
|
||||
}
|
||||
|
||||
export interface QueryResultMeta {
|
||||
[key: string]: any;
|
||||
|
||||
// Match the result to the query
|
||||
requestId?: string;
|
||||
}
|
||||
|
||||
export interface QueryResultBase {
|
||||
/**
|
||||
* Matches the query target refId
|
||||
*/
|
||||
refId?: string;
|
||||
|
||||
/**
|
||||
* Used by some backend datasources to communicate back info about the execution (generated sql, timing)
|
||||
*/
|
||||
meta?: any;
|
||||
meta?: QueryResultMeta;
|
||||
}
|
||||
|
||||
export interface Field {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ComponentClass } from 'react';
|
||||
import { TimeRange, RawTimeRange } from './time';
|
||||
import { TimeRange } from './time';
|
||||
import { PluginMeta } from './plugin';
|
||||
import { TableData, TimeSeries, SeriesData } from './data';
|
||||
|
||||
@ -94,7 +94,7 @@ export interface DataSourceApi<TQuery extends DataQuery = DataQuery> {
|
||||
/**
|
||||
* Main metrics / data query action
|
||||
*/
|
||||
query(options: DataQueryOptions<TQuery>): Promise<DataQueryResponse>;
|
||||
query(options: DataQueryRequest<TQuery>): Promise<DataQueryResponse>;
|
||||
|
||||
/**
|
||||
* Test & verify datasource settings & connection details
|
||||
@ -220,10 +220,11 @@ export interface ScopedVars {
|
||||
[key: string]: ScopedVar;
|
||||
}
|
||||
|
||||
export interface DataQueryOptions<TQuery extends DataQuery = DataQuery> {
|
||||
export interface DataQueryRequest<TQuery extends DataQuery = DataQuery> {
|
||||
requestId: string; // Used to identify results and optionally cancel the request in backendSrv
|
||||
timezone: string;
|
||||
range: TimeRange;
|
||||
rangeRaw: RawTimeRange; // Duplicate of results in range. will be deprecated eventually
|
||||
timeInfo?: string; // The query time description (blue text in the upper right)
|
||||
targets: TQuery[];
|
||||
panelId: number;
|
||||
dashboardId: number;
|
||||
@ -232,13 +233,8 @@ export interface DataQueryOptions<TQuery extends DataQuery = DataQuery> {
|
||||
intervalMs: number;
|
||||
maxDataPoints: number;
|
||||
scopedVars: ScopedVars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamps when the query starts and stops
|
||||
*/
|
||||
export interface DataRequestInfo extends DataQueryOptions {
|
||||
timeInfo?: string; // The query time description (blue text in the upper right)
|
||||
// Request Timing
|
||||
startTime: number;
|
||||
endTime?: number;
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { ComponentClass } from 'react';
|
||||
import { LoadingState, SeriesData } from './data';
|
||||
import { TimeRange } from './time';
|
||||
import { ScopedVars, DataRequestInfo, DataQueryError, LegacyResponseData } from './datasource';
|
||||
import { ScopedVars, DataQueryRequest, DataQueryError, LegacyResponseData } from './datasource';
|
||||
|
||||
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
|
||||
|
||||
export interface PanelData {
|
||||
state: LoadingState;
|
||||
series: SeriesData[];
|
||||
request?: DataRequestInfo;
|
||||
request?: DataQueryRequest;
|
||||
error?: DataQueryError;
|
||||
|
||||
// Data format expected by Angular panels
|
||||
|
@ -336,5 +336,9 @@ export class PanelModel {
|
||||
destroy() {
|
||||
this.events.emit('panel-teardown');
|
||||
this.events.removeAllListeners();
|
||||
|
||||
if (this.queryRunner) {
|
||||
this.queryRunner.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { getProcessedSeriesData, PanelQueryRunner } from './PanelQueryRunner';
|
||||
import { PanelData, DataQueryOptions } from '@grafana/ui/src/types';
|
||||
import { PanelData, DataQueryRequest } from '@grafana/ui/src/types';
|
||||
import moment from 'moment';
|
||||
|
||||
describe('PanelQueryRunner', () => {
|
||||
@ -46,7 +46,7 @@ interface ScenarioContext {
|
||||
minInterval?: string;
|
||||
events?: PanelData[];
|
||||
res?: PanelData;
|
||||
queryCalledWith?: DataQueryOptions;
|
||||
queryCalledWith?: DataQueryRequest;
|
||||
}
|
||||
|
||||
type ScenarioFn = (ctx: ScenarioContext) => void;
|
||||
@ -70,9 +70,9 @@ function describeQueryRunnerScenario(description: string, scenarioFn: ScenarioFn
|
||||
beforeEach(async () => {
|
||||
setupFn();
|
||||
|
||||
const ds: any = {
|
||||
const datasource: any = {
|
||||
interval: ctx.dsInterval,
|
||||
query: (options: DataQueryOptions) => {
|
||||
query: (options: DataQueryRequest) => {
|
||||
ctx.queryCalledWith = options;
|
||||
return Promise.resolve(response);
|
||||
},
|
||||
@ -80,8 +80,7 @@ function describeQueryRunnerScenario(description: string, scenarioFn: ScenarioFn
|
||||
};
|
||||
|
||||
const args: any = {
|
||||
ds: ds as any,
|
||||
datasource: '',
|
||||
datasource,
|
||||
minInterval: ctx.minInterval,
|
||||
widthPixels: ctx.widthPixels,
|
||||
maxDataPoints: ctx.maxDataPoints,
|
||||
|
@ -4,6 +4,7 @@ import { Subject, Unsubscribable, PartialObserver } from 'rxjs';
|
||||
|
||||
// Services & Utils
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import templateSrv from 'app/features/templating/template_srv';
|
||||
|
||||
@ -16,7 +17,7 @@ import {
|
||||
DataQuery,
|
||||
TimeRange,
|
||||
ScopedVars,
|
||||
DataRequestInfo,
|
||||
DataQueryRequest,
|
||||
SeriesData,
|
||||
DataQueryError,
|
||||
toLegacyResponseData,
|
||||
@ -25,8 +26,7 @@ import {
|
||||
} from '@grafana/ui';
|
||||
|
||||
export interface QueryRunnerOptions<TQuery extends DataQuery = DataQuery> {
|
||||
ds?: DataSourceApi<TQuery>; // if they already have the datasource, don't look it up
|
||||
datasource: string | null;
|
||||
datasource: string | DataSourceApi<TQuery>;
|
||||
queries: TQuery[];
|
||||
panelId: number;
|
||||
dashboardId?: number;
|
||||
@ -47,6 +47,11 @@ export enum PanelQueryRunnerFormat {
|
||||
both = 'both',
|
||||
}
|
||||
|
||||
let counter = 100;
|
||||
function getNextRequestId() {
|
||||
return 'Q' + counter++;
|
||||
}
|
||||
|
||||
export class PanelQueryRunner {
|
||||
private subject?: Subject<PanelData>;
|
||||
|
||||
@ -106,12 +111,12 @@ export class PanelQueryRunner {
|
||||
delayStateNotification,
|
||||
} = options;
|
||||
|
||||
const request: DataRequestInfo = {
|
||||
const request: DataQueryRequest = {
|
||||
requestId: getNextRequestId(),
|
||||
timezone,
|
||||
panelId,
|
||||
dashboardId,
|
||||
range: timeRange,
|
||||
rangeRaw: timeRange.raw,
|
||||
timeInfo,
|
||||
interval: '',
|
||||
intervalMs: 0,
|
||||
@ -121,6 +126,8 @@ export class PanelQueryRunner {
|
||||
cacheTimeout,
|
||||
startTime: Date.now(),
|
||||
};
|
||||
// Deprecated
|
||||
(request as any).rangeRaw = timeRange.raw;
|
||||
|
||||
if (!queries) {
|
||||
return this.publishUpdate({
|
||||
@ -134,7 +141,10 @@ export class PanelQueryRunner {
|
||||
let loadingStateTimeoutId = 0;
|
||||
|
||||
try {
|
||||
const ds = options.ds ? options.ds : await getDatasourceSrv().get(datasource, request.scopedVars);
|
||||
const ds =
|
||||
datasource && (datasource as any).query
|
||||
? (datasource as DataSourceApi)
|
||||
: await getDatasourceSrv().get(datasource as string, request.scopedVars);
|
||||
|
||||
const lowerIntervalLimit = minInterval ? templateSrv.replace(minInterval, request.scopedVars) : ds.interval;
|
||||
const norm = kbn.calculateInterval(timeRange, widthPixels, lowerIntervalLimit);
|
||||
@ -157,6 +167,11 @@ export class PanelQueryRunner {
|
||||
const resp = await ds.query(request);
|
||||
request.endTime = Date.now();
|
||||
|
||||
// Make sure we send something back -- called run() w/o subscribe!
|
||||
if (!(this.sendSeries || this.sendLegacy)) {
|
||||
this.sendSeries = true;
|
||||
}
|
||||
|
||||
// Make sure the response is in a supported format
|
||||
const series = this.sendSeries ? getProcessedSeriesData(resp.data) : [];
|
||||
const legacy = this.sendLegacy
|
||||
@ -214,6 +229,22 @@ export class PanelQueryRunner {
|
||||
|
||||
return this.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the panel is closed
|
||||
*/
|
||||
destroy() {
|
||||
// Tell anyone listening that we are done
|
||||
if (this.subject) {
|
||||
this.subject.complete();
|
||||
}
|
||||
|
||||
// If there are open HTTP requests, close them
|
||||
const { request } = this.data;
|
||||
if (request && request.requestId) {
|
||||
getBackendSrv().resolveCancelerIfExists(request.requestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,7 +3,7 @@ import _ from 'lodash';
|
||||
import * as dateMath from 'app/core/utils/datemath';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import { CloudWatchQuery } from './types';
|
||||
import { DataSourceApi } from '@grafana/ui/src/types';
|
||||
import { DataSourceApi, DataQueryRequest } from '@grafana/ui/src/types';
|
||||
// import * as moment from 'moment';
|
||||
|
||||
export default class CloudWatchDatasource implements DataSourceApi<CloudWatchQuery> {
|
||||
@ -23,7 +23,7 @@ export default class CloudWatchDatasource implements DataSourceApi<CloudWatchQue
|
||||
this.standardStatistics = ['Average', 'Maximum', 'Minimum', 'Sum', 'SampleCount'];
|
||||
}
|
||||
|
||||
query(options) {
|
||||
query(options: DataQueryRequest<CloudWatchQuery>) {
|
||||
options = angular.copy(options);
|
||||
options.targets = this.expandTemplateVariable(options.targets, options.scopedVars, this.templateSrv);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Types
|
||||
import {
|
||||
DataQueryOptions,
|
||||
DataQueryRequest,
|
||||
SeriesData,
|
||||
DataQueryResponse,
|
||||
DataSourceApi,
|
||||
@ -67,7 +67,7 @@ export class InputDatasource implements DataSourceApi<InputQuery> {
|
||||
});
|
||||
}
|
||||
|
||||
query(options: DataQueryOptions<InputQuery>): Promise<DataQueryResponse> {
|
||||
query(options: DataQueryRequest<InputQuery>): Promise<DataQueryResponse> {
|
||||
const results: SeriesData[] = [];
|
||||
for (const query of options.targets) {
|
||||
if (query.hide) {
|
||||
|
@ -11,7 +11,7 @@ import { makeSeriesForLogs } from 'app/core/logs_model';
|
||||
|
||||
// Types
|
||||
import { LogsStream, LogsModel } from 'app/core/logs_model';
|
||||
import { PluginMeta, DataQueryOptions } from '@grafana/ui/src/types';
|
||||
import { PluginMeta, DataQueryRequest } from '@grafana/ui/src/types';
|
||||
import { LokiQuery } from './types';
|
||||
|
||||
export const DEFAULT_MAX_LINES = 1000;
|
||||
@ -73,7 +73,7 @@ export class LokiDatasource {
|
||||
};
|
||||
}
|
||||
|
||||
async query(options: DataQueryOptions<LokiQuery>) {
|
||||
async query(options: DataQueryRequest<LokiQuery>) {
|
||||
const queryTargets = options.targets
|
||||
.filter(target => target.expr && !target.hide)
|
||||
.map(target => this.prepareQueryTarget(target, options));
|
||||
|
@ -1,13 +1,13 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
import { DataSourceApi, DataQuery, DataQueryOptions } from '@grafana/ui';
|
||||
import { DataSourceApi, DataQuery, DataQueryRequest } from '@grafana/ui';
|
||||
import DatasourceSrv from 'app/features/plugins/datasource_srv';
|
||||
|
||||
class MixedDatasource implements DataSourceApi<DataQuery> {
|
||||
/** @ngInject */
|
||||
constructor(private datasourceSrv: DatasourceSrv) {}
|
||||
|
||||
query(options: DataQueryOptions<DataQuery>) {
|
||||
query(options: DataQueryRequest<DataQuery>) {
|
||||
const sets = _.groupBy(options.targets, 'datasource');
|
||||
const promises: any = _.map(sets, (targets: DataQuery[]) => {
|
||||
const dsName = targets[0].datasource;
|
||||
|
@ -15,7 +15,7 @@ import { expandRecordingRules } from './language_utils';
|
||||
|
||||
// Types
|
||||
import { PromQuery } from './types';
|
||||
import { DataQueryOptions, DataSourceApi, AnnotationEvent } from '@grafana/ui/src/types';
|
||||
import { DataQueryRequest, DataSourceApi, AnnotationEvent } from '@grafana/ui/src/types';
|
||||
import { ExploreUrlState } from 'app/types/explore';
|
||||
|
||||
export class PrometheusDatasource implements DataSourceApi<PromQuery> {
|
||||
@ -120,7 +120,7 @@ export class PrometheusDatasource implements DataSourceApi<PromQuery> {
|
||||
return this.templateSrv.variableExists(target.expr);
|
||||
}
|
||||
|
||||
query(options: DataQueryOptions<PromQuery>) {
|
||||
query(options: DataQueryRequest<PromQuery>) {
|
||||
const start = this.getPrometheusTime(options.range.from, false);
|
||||
const end = this.getPrometheusTime(options.range.to, true);
|
||||
|
||||
|
@ -3,7 +3,7 @@ import appEvents from 'app/core/app_events';
|
||||
import _ from 'lodash';
|
||||
import StackdriverMetricFindQuery from './StackdriverMetricFindQuery';
|
||||
import { StackdriverQuery, MetricDescriptor } from './types';
|
||||
import { DataSourceApi, DataQueryOptions } from '@grafana/ui/src/types';
|
||||
import { DataSourceApi, DataQueryRequest } from '@grafana/ui/src/types';
|
||||
|
||||
export default class StackdriverDatasource implements DataSourceApi<StackdriverQuery> {
|
||||
id: number;
|
||||
@ -108,7 +108,7 @@ export default class StackdriverDatasource implements DataSourceApi<StackdriverQ
|
||||
return unit;
|
||||
}
|
||||
|
||||
async query(options: DataQueryOptions<StackdriverQuery>) {
|
||||
async query(options: DataQueryRequest<StackdriverQuery>) {
|
||||
const result = [];
|
||||
const data = await this.getTimeSeries(options);
|
||||
if (data.results) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import { DataSourceApi, DataQueryOptions, TableData, TimeSeries } from '@grafana/ui';
|
||||
import { DataSourceApi, DataQueryRequest, TableData, TimeSeries } from '@grafana/ui';
|
||||
import { TestDataQuery, Scenario } from './types';
|
||||
|
||||
type TestData = TimeSeries | TableData;
|
||||
@ -16,7 +16,7 @@ export class TestDataDatasource implements DataSourceApi<TestDataQuery> {
|
||||
this.id = instanceSettings.id;
|
||||
}
|
||||
|
||||
query(options: DataQueryOptions<TestDataQuery>) {
|
||||
query(options: DataQueryRequest<TestDataQuery>) {
|
||||
const queries = _.filter(options.targets, item => {
|
||||
return item.hide !== true;
|
||||
}).map(item => {
|
||||
@ -45,8 +45,11 @@ export class TestDataDatasource implements DataSourceApi<TestDataQuery> {
|
||||
to: options.range.to.valueOf().toString(),
|
||||
queries: queries,
|
||||
},
|
||||
|
||||
// This sets up a cancel token
|
||||
requestId: options.requestId,
|
||||
})
|
||||
.then(res => {
|
||||
.then((res: any) => {
|
||||
const data: TestData[] = [];
|
||||
|
||||
// Returns data in the order it was asked for.
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { DataQueryOptions, DataQuery } from '@grafana/ui';
|
||||
import { DataQueryRequest, DataQuery } from '@grafana/ui';
|
||||
import moment from 'moment';
|
||||
|
||||
export function getQueryOptions<TQuery extends DataQuery>(
|
||||
options: Partial<DataQueryOptions<TQuery>>
|
||||
): DataQueryOptions<TQuery> {
|
||||
options: Partial<DataQueryRequest<TQuery>>
|
||||
): DataQueryRequest<TQuery> {
|
||||
const raw = { from: 'now', to: 'now-1h' };
|
||||
const range = { from: moment(), to: moment(), raw: raw };
|
||||
|
||||
const defaults: DataQueryOptions<TQuery> = {
|
||||
const defaults: DataQueryRequest<TQuery> = {
|
||||
requestId: 'TEST',
|
||||
range: range,
|
||||
rangeRaw: raw,
|
||||
targets: [],
|
||||
scopedVars: {},
|
||||
timezone: 'browser',
|
||||
@ -18,6 +18,7 @@ export function getQueryOptions<TQuery extends DataQuery>(
|
||||
interval: '60s',
|
||||
intervalMs: 60000,
|
||||
maxDataPoints: 500,
|
||||
startTime: 0,
|
||||
};
|
||||
|
||||
Object.assign(defaults, options);
|
||||
|
Loading…
Reference in New Issue
Block a user