mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 06:01:49 +08:00
Expressions: Add support for variables (#41778)
This commit is contained in:
@ -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.
|
||||
|
37
public/app/features/expressions/ExpressionDatasource.test.ts
Normal file
37
public/app/features/expressions/ExpressionDatasource.test.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
});
|
@ -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
|
||||
|
Reference in New Issue
Block a user