diff --git a/packages/grafana-data/src/types/datasource.ts b/packages/grafana-data/src/types/datasource.ts index e38a5108a8c..f6cfcdb874f 100644 --- a/packages/grafana-data/src/types/datasource.ts +++ b/packages/grafana-data/src/types/datasource.ts @@ -4,12 +4,13 @@ import { GrafanaPlugin, PluginMeta } from './plugin'; import { PanelData } from './panel'; import { LogRowModel } from './logs'; import { AnnotationEvent, AnnotationSupport } from './annotations'; -import { KeyValue, LoadingState, TableData, TimeSeries, DataTopic } from './data'; +import { DataTopic, KeyValue, LoadingState, TableData, TimeSeries } from './data'; import { DataFrame, DataFrameDTO } from './dataFrame'; import { RawTimeRange, TimeRange } from './time'; import { ScopedVars } from './ScopedVars'; import { CoreApp } from './app'; import { LiveChannelSupport } from './live'; +import { CustomVariableSupport, DataSourceVariableSupport, StandardVariableSupport } from './variables'; export interface DataSourcePluginOptionsEditorProps { options: DataSourceSettings; @@ -79,6 +80,9 @@ export class DataSourcePlugin< return this; } + /* + * @deprecated -- prefer using {@link StandardVariableSupport} or {@link CustomVariableSupport} or {@link DataSourceVariableSupport} in data source instead + * */ setVariableQueryEditor(VariableQueryEditor: any) { this.components.VariableQueryEditor = VariableQueryEditor; return this; @@ -295,6 +299,15 @@ export abstract class DataSourceApi< * @alpha -- experimental */ channelSupport?: LiveChannelSupport; + + /** + * Defines new variable support + * @alpha -- experimental + */ + variables?: + | StandardVariableSupport> + | CustomVariableSupport> + | DataSourceVariableSupport>; } export interface MetadataInspectorProps< @@ -311,12 +324,13 @@ export interface MetadataInspectorProps< export interface QueryEditorProps< DSType extends DataSourceApi, TQuery extends DataQuery = DataQuery, - TOptions extends DataSourceJsonData = DataSourceJsonData + TOptions extends DataSourceJsonData = DataSourceJsonData, + TVQuery extends DataQuery = TQuery > { datasource: DSType; - query: TQuery; + query: TVQuery; onRunQuery: () => void; - onChange: (value: TQuery) => void; + onChange: (value: TVQuery) => void; onBlur?: () => void; /** * Contains query response filtered by refId of QueryResultBase and possible query error diff --git a/packages/grafana-data/src/types/index.ts b/packages/grafana-data/src/types/index.ts index 7a1ac746ed2..8d31ee8b3cc 100644 --- a/packages/grafana-data/src/types/index.ts +++ b/packages/grafana-data/src/types/index.ts @@ -28,5 +28,6 @@ export * from './trace'; export * from './explore'; export * from './legacyEvents'; export * from './live'; +export * from './variables'; export { GrafanaConfig, BuildInfo, FeatureToggles, LicenseInfo } from './config'; diff --git a/packages/grafana-data/src/types/variables.ts b/packages/grafana-data/src/types/variables.ts new file mode 100644 index 00000000000..5c0aac20832 --- /dev/null +++ b/packages/grafana-data/src/types/variables.ts @@ -0,0 +1,99 @@ +import { ComponentType } from 'react'; +import { Observable } from 'rxjs'; + +import { + DataQuery, + DataQueryRequest, + DataQueryResponse, + DataSourceApi, + DataSourceJsonData, + DataSourceOptionsType, + DataSourceQueryType, + QueryEditorProps, +} from './datasource'; + +/** + * Enum with the different variable support types + * + * @alpha -- experimental + */ +export enum VariableSupportType { + Legacy = 'legacy', + Standard = 'standard', + Custom = 'custom', + Datasource = 'datasource', +} + +/** + * Base class for VariableSupport classes + * + * @alpha -- experimental + */ +export abstract class VariableSupportBase< + DSType extends DataSourceApi, + TQuery extends DataQuery = DataSourceQueryType, + TOptions extends DataSourceJsonData = DataSourceOptionsType +> { + abstract getType(): VariableSupportType; +} + +/** + * Extend this class in a data source plugin to use the standard query editor for Query variables + * + * @alpha -- experimental + */ +export abstract class StandardVariableSupport< + DSType extends DataSourceApi, + TQuery extends DataQuery = DataSourceQueryType, + TOptions extends DataSourceJsonData = DataSourceOptionsType +> extends VariableSupportBase { + getType(): VariableSupportType { + return VariableSupportType.Standard; + } + + abstract toDataQuery(query: StandardVariableQuery): TQuery; + query?(request: DataQueryRequest): Observable; +} + +/** + * Extend this class in a data source plugin to use a customized query editor for Query variables + * + * @alpha -- experimental + */ +export abstract class CustomVariableSupport< + DSType extends DataSourceApi, + VariableQuery extends DataQuery = any, + TQuery extends DataQuery = DataSourceQueryType, + TOptions extends DataSourceJsonData = DataSourceOptionsType +> extends VariableSupportBase { + getType(): VariableSupportType { + return VariableSupportType.Custom; + } + + abstract editor: ComponentType>; + abstract query(request: DataQueryRequest): Observable; +} + +/** + * Extend this class in a data source plugin to use the query editor in the data source plugin for Query variables + * + * @alpha -- experimental + */ +export abstract class DataSourceVariableSupport< + DSType extends DataSourceApi, + TQuery extends DataQuery = DataSourceQueryType, + TOptions extends DataSourceJsonData = DataSourceOptionsType +> extends VariableSupportBase { + getType(): VariableSupportType { + return VariableSupportType.Datasource; + } +} + +/** + * Defines the standard DatQuery used by data source plugins that implement StandardVariableSupport + * + * @alpha -- experimental + */ +export interface StandardVariableQuery extends DataQuery { + query: string; +} diff --git a/public/app/app.ts b/public/app/app.ts index 9058fc9e9e0..f523cff4440 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -28,10 +28,10 @@ import _ from 'lodash'; import { AppEvents, setLocale, + setTimeZoneResolver, standardEditorsRegistry, standardFieldConfigEditorRegistry, standardTransformersRegistry, - setTimeZoneResolver, } from '@grafana/data'; import appEvents from 'app/core/app_events'; import { checkBrowserCompatibility } from 'app/core/utils/browser'; @@ -45,12 +45,13 @@ import { reportPerformance } from './core/services/echo/EchoSrv'; import { PerformanceBackend } from './core/services/echo/backends/PerformanceBackend'; import 'app/routes/GrafanaCtrl'; import 'app/features/all'; -import { getStandardFieldConfigs, getStandardOptionEditors, getScrollbarWidth } from '@grafana/ui'; +import { getScrollbarWidth, getStandardFieldConfigs, getStandardOptionEditors } from '@grafana/ui'; import { getDefaultVariableAdapters, variableAdapters } from './features/variables/adapters'; import { initDevFeatures } from './dev'; import { getStandardTransformers } from 'app/core/utils/standardTransformers'; import { SentryEchoBackend } from './core/services/echo/backends/sentry/SentryBackend'; import { monkeyPatchInjectorWithPreAssignedBindings } from './core/injectorMonkeyPatch'; +import { setVariableQueryRunner, VariableQueryRunner } from './features/variables/query/VariableQueryRunner'; // add move to lodash for backward compatabiltiy // @ts-ignore @@ -101,6 +102,7 @@ export class GrafanaApp { standardFieldConfigEditorRegistry.setInit(getStandardFieldConfigs); standardTransformersRegistry.setInit(getStandardTransformers); variableAdapters.setInit(getDefaultVariableAdapters); + setVariableQueryRunner(new VariableQueryRunner()); app.config( ( diff --git a/public/app/features/dashboard/state/runRequest.ts b/public/app/features/dashboard/state/runRequest.ts index f5e6f8ea016..327ba76190a 100644 --- a/public/app/features/dashboard/state/runRequest.ts +++ b/public/app/features/dashboard/state/runRequest.ts @@ -1,27 +1,27 @@ // Libraries -import { Observable, of, timer, merge, from } from 'rxjs'; -import { map as isArray, isString } from 'lodash'; -import { map, catchError, takeUntil, mapTo, share, finalize, tap } from 'rxjs/operators'; +import { from, merge, Observable, of, timer } from 'rxjs'; +import { isString, map as isArray } from 'lodash'; +import { catchError, finalize, map, mapTo, share, takeUntil, tap } from 'rxjs/operators'; // Utils & Services import { backendSrv } from 'app/core/services/backend_srv'; // Types import { - DataSourceApi, + DataFrame, + DataQueryError, DataQueryRequest, - PanelData, DataQueryResponse, DataQueryResponseData, - DataQueryError, - LoadingState, - dateMath, - toDataFrame, - DataFrame, + DataSourceApi, DataTopic, + dateMath, guessFieldTypes, + LoadingState, + PanelData, + toDataFrame, } from '@grafana/data'; import { toDataQueryError } from '@grafana/runtime'; import { emitDataRequestEvent } from './analyticsProcessor'; -import { ExpressionDatasourceID, expressionDatasource } from 'app/features/expressions/ExpressionDatasource'; +import { expressionDatasource, ExpressionDatasourceID } from 'app/features/expressions/ExpressionDatasource'; import { ExpressionQuery } from 'app/features/expressions/types'; type MapOfResponsePackets = { [str: string]: DataQueryResponse }; @@ -97,7 +97,11 @@ export function processResponsePacket(packet: DataQueryResponse, state: RunningQ * Will emit a loading state if no response after 50ms * Cancel any still running network requests on unsubscribe (using request.requestId) */ -export function runRequest(datasource: DataSourceApi, request: DataQueryRequest): Observable { +export function runRequest( + datasource: DataSourceApi, + request: DataQueryRequest, + queryFunction?: typeof datasource.query +): Observable { let state: RunningQueryState = { panelData: { state: LoadingState.Loading, @@ -115,7 +119,7 @@ export function runRequest(datasource: DataSourceApi, request: DataQueryRequest) return of(state.panelData); } - const dataObservable = callQueryMethod(datasource, request).pipe( + const dataObservable = callQueryMethod(datasource, request, queryFunction).pipe( // Transform response packets into PanelData with merged results map((packet: DataQueryResponse) => { if (!isArray(packet.data)) { @@ -157,7 +161,11 @@ function cancelNetworkRequestsOnUnsubscribe(req: DataQueryRequest) { }; } -export function callQueryMethod(datasource: DataSourceApi, request: DataQueryRequest) { +export function callQueryMethod( + datasource: DataSourceApi, + request: DataQueryRequest, + queryFunction?: typeof datasource.query +) { // If any query has an expression, use the expression endpoint for (const target of request.targets) { if (target.datasource === ExpressionDatasourceID) { @@ -166,7 +174,7 @@ export function callQueryMethod(datasource: DataSourceApi, request: DataQueryReq } // Otherwise it is a standard datasource request - const returnVal = datasource.query(request); + const returnVal = queryFunction ? queryFunction(request) : datasource.query(request); return from(returnVal); } diff --git a/public/app/features/plugins/variableQueryEditorLoader.tsx b/public/app/features/plugins/variableQueryEditorLoader.tsx index afe9416a93f..2c2b6e14790 100644 --- a/public/app/features/plugins/variableQueryEditorLoader.tsx +++ b/public/app/features/plugins/variableQueryEditorLoader.tsx @@ -2,7 +2,7 @@ import coreModule from 'app/core/core_module'; import { importDataSourcePlugin } from './plugin_loader'; import React from 'react'; import ReactDOM from 'react-dom'; -import DefaultVariableQueryEditor from '../variables/editor/DefaultVariableQueryEditor'; +import { LegacyVariableQueryEditor } from '../variables/editor/LegacyVariableQueryEditor'; import { DataSourcePluginMeta } from '@grafana/data'; import { TemplateSrv } from '@grafana/runtime'; @@ -11,7 +11,7 @@ async function loadComponent(meta: DataSourcePluginMeta) { if (dsPlugin.components.VariableQueryEditor) { return dsPlugin.components.VariableQueryEditor; } else { - return DefaultVariableQueryEditor; + return LegacyVariableQueryEditor; } } diff --git a/public/app/features/variables/editor/DefaultVariableQueryEditor.tsx b/public/app/features/variables/editor/DefaultVariableQueryEditor.tsx deleted file mode 100644 index 2135747a15c..00000000000 --- a/public/app/features/variables/editor/DefaultVariableQueryEditor.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { PureComponent } from 'react'; -import { VariableQueryProps } from 'app/types/plugins'; -import { selectors } from '@grafana/e2e-selectors'; - -export default class DefaultVariableQueryEditor extends PureComponent { - constructor(props: VariableQueryProps) { - super(props); - this.state = { value: props.query }; - } - - onChange = (event: React.FormEvent) => { - this.setState({ value: event.currentTarget.value }); - }; - - onBlur = (event: React.FormEvent) => { - this.props.onChange(event.currentTarget.value, event.currentTarget.value); - }; - - getLineCount() { - const { value } = this.state; - - if (typeof value === 'string') { - return value.split('\n').length; - } - - return 1; - } - - render() { - return ( -
- Query -