Expressions: Add support for variables (#41778)

This commit is contained in:
Todd Treece
2022-02-17 10:50:22 -05:00
committed by GitHub
parent 42e547d27f
commit c8bb01d1ad
3 changed files with 71 additions and 4 deletions

View File

@ -16,8 +16,6 @@ Expressions are primarily used by the new [Grafana 8 alerts]({{< relref "../../.
> **Note:** Expressions do not work with legacy dashboard alerts.
> **Note:** Expressions do not work with dashboard variables.
Expressions are meant to augment data sources by enabling queries from different data sources to be combined or by providing operations unavailable in a data source.
> **Note:** When possible, you should do data processing inside the data source. Copying data from storage to the Grafana server for processing is inefficient, so expressions are targeted at lightweight data processing.

View File

@ -0,0 +1,37 @@
import { DataSourceInstanceSettings } from '@grafana/data';
import { backendSrv } from 'app/core/services/backend_srv';
import { ExpressionDatasourceApi } from './ExpressionDatasource';
import { ExpressionQueryType } from './types';
jest.mock('@grafana/runtime', () => ({
...(jest.requireActual('@grafana/runtime') as unknown as object),
getBackendSrv: () => backendSrv,
getTemplateSrv: () => ({
replace: (val: string) => (val ? val.replace('$input', '10').replace('$window', '10s') : val),
}),
}));
describe('ExpressionDatasourceApi', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('expression queries with template variables', () => {
it('should interpolate template variables in expression query', () => {
const ds = new ExpressionDatasourceApi({} as DataSourceInstanceSettings);
const query = ds.applyTemplateVariables(
{ type: ExpressionQueryType.math, refId: 'B', expression: '$input + 5 + $A' },
{}
);
expect(query.expression).toBe('10 + 5 + $A');
});
it('should interpolate template variables in expression query', () => {
const ds = new ExpressionDatasourceApi({} as DataSourceInstanceSettings);
const query = ds.applyTemplateVariables(
{ type: ExpressionQueryType.resample, refId: 'B', window: '$window' },
{}
);
expect(query.window).toBe('10s');
});
});
});

View File

@ -1,8 +1,16 @@
import { DataSourceInstanceSettings, DataSourcePluginMeta, PluginType } from '@grafana/data';
import {
DataQueryRequest,
DataQueryResponse,
DataSourceInstanceSettings,
DataSourcePluginMeta,
PluginType,
ScopedVars,
} from '@grafana/data';
import { ExpressionQuery, ExpressionQueryType } from './types';
import { ExpressionQueryEditor } from './ExpressionQueryEditor';
import { DataSourceWithBackend } from '@grafana/runtime';
import { DataSourceWithBackend, getDataSourceSrv, getTemplateSrv } from '@grafana/runtime';
import { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend';
import { Observable, from, mergeMap } from 'rxjs';
/**
* This is a singleton instance that just pretends to be a DataSource
@ -12,10 +20,34 @@ export class ExpressionDatasourceApi extends DataSourceWithBackend<ExpressionQue
super(instanceSettings);
}
applyTemplateVariables(query: ExpressionQuery, scopedVars: ScopedVars): Record<string, any> {
const templateSrv = getTemplateSrv();
return {
...query,
expression: templateSrv.replace(query.expression, scopedVars),
window: templateSrv.replace(query.window, scopedVars),
};
}
getCollapsedText(query: ExpressionQuery) {
return `Expression: ${query.type}`;
}
query(request: DataQueryRequest<ExpressionQuery>): Observable<DataQueryResponse> {
let targets = request.targets.map(async (query: ExpressionQuery): Promise<ExpressionQuery> => {
const ds = await getDataSourceSrv().get(query.datasource);
if (!ds.interpolateVariablesInQueries) {
return query;
}
return ds?.interpolateVariablesInQueries([query], {})[0] as ExpressionQuery;
});
let sub = from(Promise.all(targets));
return sub.pipe(mergeMap((t) => super.query({ ...request, targets: t })));
}
newQuery(query?: Partial<ExpressionQuery>): ExpressionQuery {
return {
refId: '--', // Replaced with query