grafana/public/app/features/variables/query/queryRunners.ts
Hugo Häggmark 112a755e18
Variables: Adds new Api that allows proper QueryEditors for Query variables (#28217)
* Initial

* WIP

* wip

* Refactor: fixing types

* Refactor: Fixed more typings

* Feature: Moves TestData to new API

* Feature: Moves CloudMonitoringDatasource to new API

* Feature: Moves PrometheusDatasource to new Variables API

* Refactor: Clean up comments

* Refactor: changes to QueryEditorProps instead

* Refactor: cleans up testdata, prometheus and cloud monitoring variable support

* Refactor: adds variableQueryRunner

* Refactor: adds props to VariableQueryEditor

* Refactor: reverted Loki editor

* Refactor: refactor queryrunner into smaller pieces

* Refactor: adds upgrade query thunk

* Tests: Updates old tests

* Docs: fixes build errors for exported api

* Tests: adds guard tests

* Tests: adds QueryRunner tests

* Tests: fixes broken tests

* Tests: adds variableQueryObserver tests

* Test: adds tests for operator functions

* Test: adds VariableQueryRunner tests

* Refactor: renames dataSource

* Refactor: adds definition for standard variable support

* Refactor: adds cancellation to OptionPicker

* Refactor: changes according to Dominiks suggestion

* Refactor:tt

* Refactor: adds tests for factories

* Refactor: restructuring a bit

* Refactor: renames variableQueryRunner.ts

* Refactor: adds quick exit when runRequest returns errors

* Refactor: using TextArea from grafana/ui

* Refactor: changed from interfaces to classes instead

* Tests: fixes broken test

* Docs: fixes doc issue count

* Docs: fixes doc issue count

* Refactor: Adds check for self referencing queries

* Tests: fixed unused variable

* Refactor: Changes comments
2020-11-18 15:10:32 +01:00

179 lines
4.9 KiB
TypeScript

import { from, Observable, of } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import {
DataQuery,
DataQueryRequest,
DataSourceApi,
DefaultTimeRange,
LoadingState,
PanelData,
VariableSupportType,
} from '@grafana/data';
import { QueryVariableModel } from '../types';
import {
hasCustomVariableSupport,
hasDatasourceVariableSupport,
hasLegacyVariableSupport,
hasStandardVariableSupport,
} from '../guard';
import { getLegacyQueryOptions } from '../utils';
import { TimeSrv } from '../../dashboard/services/TimeSrv';
export interface RunnerArgs {
variable: QueryVariableModel;
datasource: DataSourceApi;
timeSrv: TimeSrv;
runRequest: (
datasource: DataSourceApi,
request: DataQueryRequest,
queryFunction?: typeof datasource.query
) => Observable<PanelData>;
searchFilter?: string;
}
type GetTargetArgs = { datasource: DataSourceApi; variable: QueryVariableModel };
export interface QueryRunner {
type: VariableSupportType;
canRun: (dataSource: DataSourceApi) => boolean;
getTarget: (args: GetTargetArgs) => DataQuery;
runRequest: (args: RunnerArgs, request: DataQueryRequest) => Observable<PanelData>;
}
export class QueryRunners {
private readonly runners: QueryRunner[];
constructor() {
this.runners = [
new LegacyQueryRunner(),
new StandardQueryRunner(),
new CustomQueryRunner(),
new DatasourceQueryRunner(),
];
}
getRunnerForDatasource(datasource: DataSourceApi): QueryRunner {
const runner = this.runners.find(runner => runner.canRun(datasource));
if (runner) {
return runner;
}
throw new Error("Couldn't find a query runner that matches supplied arguments.");
}
}
class LegacyQueryRunner implements QueryRunner {
type = VariableSupportType.Legacy;
canRun(dataSource: DataSourceApi) {
return hasLegacyVariableSupport(dataSource);
}
getTarget({ datasource, variable }: GetTargetArgs) {
if (hasLegacyVariableSupport(datasource)) {
return variable.query;
}
throw new Error("Couldn't create a target with supplied arguments.");
}
runRequest({ datasource, variable, searchFilter, timeSrv }: RunnerArgs, request: DataQueryRequest) {
if (!hasLegacyVariableSupport(datasource)) {
return getEmptyMetricFindValueObservable();
}
const queryOptions: any = getLegacyQueryOptions(variable, searchFilter, timeSrv);
return from(datasource.metricFindQuery(variable.query, queryOptions)).pipe(
mergeMap(values => {
if (!values || !values.length) {
return getEmptyMetricFindValueObservable();
}
const series: any = values;
return of({ series, state: LoadingState.Done, timeRange: DefaultTimeRange });
})
);
}
}
class StandardQueryRunner implements QueryRunner {
type = VariableSupportType.Standard;
canRun(dataSource: DataSourceApi) {
return hasStandardVariableSupport(dataSource);
}
getTarget({ datasource, variable }: GetTargetArgs) {
if (hasStandardVariableSupport(datasource)) {
return datasource.variables.toDataQuery(variable.query);
}
throw new Error("Couldn't create a target with supplied arguments.");
}
runRequest({ datasource, runRequest }: RunnerArgs, request: DataQueryRequest) {
if (!hasStandardVariableSupport(datasource)) {
return getEmptyMetricFindValueObservable();
}
if (!datasource.variables.query) {
return runRequest(datasource, request);
}
return runRequest(datasource, request, datasource.variables.query);
}
}
class CustomQueryRunner implements QueryRunner {
type = VariableSupportType.Custom;
canRun(dataSource: DataSourceApi) {
return hasCustomVariableSupport(dataSource);
}
getTarget({ datasource, variable }: GetTargetArgs) {
if (hasCustomVariableSupport(datasource)) {
return variable.query;
}
throw new Error("Couldn't create a target with supplied arguments.");
}
runRequest({ datasource, runRequest }: RunnerArgs, request: DataQueryRequest) {
if (!hasCustomVariableSupport(datasource)) {
return getEmptyMetricFindValueObservable();
}
return runRequest(datasource, request, datasource.variables.query);
}
}
class DatasourceQueryRunner implements QueryRunner {
type = VariableSupportType.Datasource;
canRun(dataSource: DataSourceApi) {
return hasDatasourceVariableSupport(dataSource);
}
getTarget({ datasource, variable }: GetTargetArgs) {
if (hasDatasourceVariableSupport(datasource)) {
return variable.query;
}
throw new Error("Couldn't create a target with supplied arguments.");
}
runRequest({ datasource, runRequest }: RunnerArgs, request: DataQueryRequest) {
if (!hasDatasourceVariableSupport(datasource)) {
return getEmptyMetricFindValueObservable();
}
return runRequest(datasource, request);
}
}
function getEmptyMetricFindValueObservable(): Observable<PanelData> {
return of({ state: LoadingState.Done, series: [], timeRange: DefaultTimeRange });
}