Files
grafana/public/app/features/expressions/ExpressionDatasource.ts
Kristina 5bfed408ed SQL Expressions: Enable Auto-complete (#106511)
* First draft autocomplete

* Better naming

* Change suggestion to table/column population

* Remove all suggestions, just use table/column population but trigger on space

* Gate feature flag behind a feature flag

* Reorganize and add function list

* Add blurb about autocomplete to docs

* Update public/app/features/expressions/utils/metaSqlExpr.ts

Co-authored-by: Alex Spencer <52186778+alexjonspencer1@users.noreply.github.com>

* Add try catch, remove promise resolve

---------

Co-authored-by: Alex Spencer <52186778+alexjonspencer1@users.noreply.github.com>
2025-07-23 16:49:58 -05:00

152 lines
4.2 KiB
TypeScript

import { from, lastValueFrom, map, mergeMap, Observable } from 'rxjs';
import {
DataFrame,
DataQueryRequest,
DataQueryResponse,
DataSourceInstanceSettings,
DataSourcePluginMeta,
PluginType,
ScopedVars,
TimeRange,
} from '@grafana/data';
import { SQLQuery } from '@grafana/plugin-ui';
import {
BackendDataSourceResponse,
DataSourceWithBackend,
FetchResponse,
getBackendSrv,
getDataSourceSrv,
getTemplateSrv,
toDataQueryResponse,
} from '@grafana/runtime';
import { ExpressionDatasourceRef } from '@grafana/runtime/internal';
import { DataQuery } from '@grafana/schema/dist/esm/index';
import icnDatasourceSvg from 'img/icn-datasource.svg';
import { ExpressionQueryEditor } from './ExpressionQueryEditor';
import { ExpressionDatasourceUID, ExpressionQuery, ExpressionQueryType } from './types';
/**
* This is a singleton instance that just pretends to be a DataSource
*/
export class ExpressionDatasourceApi extends DataSourceWithBackend<ExpressionQuery> {
constructor(public instanceSettings: DataSourceInstanceSettings) {
super(instanceSettings);
}
applyTemplateVariables(query: ExpressionQuery, scopedVars: ScopedVars) {
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], request.scopedVars, request.filters)[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
datasource: ExpressionDatasourceRef,
type: query?.type ?? ExpressionQueryType.math,
...query,
};
}
runMetaSQLExprQuery(request: Partial<SQLQuery>, range: TimeRange, queries: DataQuery[]): Promise<DataFrame> {
const refId = request.refId || 'meta';
const metaSqlExpressionQuery: ExpressionQuery = {
window: '',
hide: false,
expression: request.rawSql,
datasource: ExpressionDatasourceRef,
refId,
type: ExpressionQueryType.sql,
};
return lastValueFrom(
getBackendSrv()
.fetch<BackendDataSourceResponse>({
url: '/api/ds/query',
method: 'POST',
headers: this.getRequestHeaders(),
data: {
from: range.from.valueOf().toString(),
to: range.to.valueOf().toString(),
queries: [...queries, metaSqlExpressionQuery],
},
requestId: refId,
})
.pipe(
map((res: FetchResponse<BackendDataSourceResponse>) => {
const rsp = toDataQueryResponse(res, queries);
return rsp.data[0] ?? { fields: [] };
})
)
);
}
}
export const instanceSettings: DataSourceInstanceSettings = {
id: -100,
uid: ExpressionDatasourceUID,
name: ExpressionDatasourceRef.name,
type: ExpressionDatasourceRef.type,
access: 'proxy',
meta: {
baseUrl: '',
module: '',
type: PluginType.datasource,
name: ExpressionDatasourceRef.type,
id: ExpressionDatasourceRef.type,
info: {
author: {
name: 'Grafana Labs',
},
logos: {
small: icnDatasourceSvg,
large: icnDatasourceSvg,
},
description: 'Adds expression support to Grafana',
screenshots: [],
links: [],
updated: '',
version: '',
},
},
jsonData: {},
readOnly: true,
};
export const dataSource = new ExpressionDatasourceApi(instanceSettings);
dataSource.meta = {
id: ExpressionDatasourceRef.type,
info: {
logos: {
small: icnDatasourceSvg,
large: icnDatasourceSvg,
},
},
} as DataSourcePluginMeta;
dataSource.components = {
QueryEditor: ExpressionQueryEditor,
};