Commit a0d43de7 by Ryan McKinley Committed by Dominik Prokop

Backend Plugins: add a common implementation (#21408)

* add common backend

* use const for range

* likely not differnt

* send the right orgId

* Add DataSourceWithBackend to @grafana/runtime mock in root reducer test

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
parent d135f122
......@@ -3,3 +3,5 @@ export * from './config';
export * from './types';
export { loadPluginCss, SystemJS } from './utils/plugin';
export { reportMetaAnalytics } from './utils/analytics';
export { DataSourceWithBackend } from './utils/DataSourceWithBackend';
import {
DataSourceApi,
DataQueryRequest,
DataQueryResponse,
DataSourceInstanceSettings,
DataQuery,
DataSourceJsonData,
} from '@grafana/data';
import { Observable, from } from 'rxjs';
import { config } from '..';
import { getBackendSrv } from '../services';
// Ideally internal (exported for consistency)
const ExpressionDatasourceID = '__expr__';
export class DataSourceWithBackend<
TQuery extends DataQuery = DataQuery,
TOptions extends DataSourceJsonData = DataSourceJsonData
> extends DataSourceApi<TQuery, TOptions> {
constructor(instanceSettings: DataSourceInstanceSettings<TOptions>) {
super(instanceSettings);
}
/**
* Ideally final -- any other implementation would be wrong!
*/
query(request: DataQueryRequest): Observable<DataQueryResponse> {
const { targets, intervalMs, maxDataPoints, range } = request;
let expressionCount = 0;
const orgId = config.bootData.user.orgId;
const queries = targets.map(q => {
if (q.datasource === ExpressionDatasourceID) {
expressionCount++;
return {
...q,
datasourceId: this.id,
orgId,
};
}
const dsName = q.datasource && q.datasource !== 'default' ? q.datasource : config.defaultDatasource;
const ds = config.datasources[dsName];
if (!ds) {
throw new Error('Unknown Datasource: ' + q.datasource);
}
return {
...q,
datasourceId: ds.id,
intervalMs,
maxDataPoints,
orgId,
};
});
const body: any = {
expressionCount,
queries,
};
if (range) {
body.range = range;
body.from = range.from.valueOf().toString();
body.to = range.to.valueOf().toString();
}
const req: Promise<DataQueryResponse> = getBackendSrv()
.post('/api/ds/query', body)
.then((rsp: any) => {
return this.toDataQueryResponse(rsp);
});
return from(req);
}
/**
* This makes the arrow libary loading async.
*/
async toDataQueryResponse(rsp: any): Promise<DataQueryResponse> {
const { resultsToDataFrames } = await import(
/* webpackChunkName: "apache-arrow-util" */ '@grafana/data/src/dataframe/ArrowDataFrame'
);
return { data: resultsToDataFrames(rsp) };
}
testDatasource() {
// TODO, this will call the backend healthcheck endpoint
return Promise.resolve({});
}
}
......@@ -14,6 +14,7 @@ jest.mock('@grafana/runtime', () => ({
user: {},
},
},
DataSourceWithBackend: jest.fn(),
}));
describe('recursiveCleanState', () => {
......
import {
DataSourceApi,
DataQueryRequest,
DataQueryResponse,
DataSourceInstanceSettings,
DataSourcePluginMeta,
} from '@grafana/data';
import { DataSourceInstanceSettings, DataSourcePluginMeta } from '@grafana/data';
import { ExpressionQuery, GELQueryType } from './types';
import { ExpressionQueryEditor } from './ExpressionQueryEditor';
import { Observable, from } from 'rxjs';
import { config } from '@grafana/runtime';
import { getBackendSrv } from 'app/core/services/backend_srv';
import { DataSourceWithBackend } from '@grafana/runtime';
/**
* This is a singleton instance that just pretends to be a DataSource
*/
export class ExpressionDatasourceApi extends DataSourceApi<ExpressionQuery> {
export class ExpressionDatasourceApi extends DataSourceWithBackend<ExpressionQuery> {
constructor(instanceSettings: DataSourceInstanceSettings) {
super(instanceSettings);
}
......@@ -23,61 +15,6 @@ export class ExpressionDatasourceApi extends DataSourceApi<ExpressionQuery> {
return `Expression: ${query.type}`;
}
query(request: DataQueryRequest): Observable<DataQueryResponse> {
const { targets, intervalMs, maxDataPoints, range } = request;
let expressionCount = 0;
const orgId = (window as any).grafanaBootData.user.orgId;
const queries = targets.map(q => {
if (q.datasource === ExpressionDatasourceID) {
expressionCount++;
return {
...q,
datasourceId: this.id,
orgId,
};
}
const dsName = q.datasource && q.datasource !== 'default' ? q.datasource : config.defaultDatasource;
const ds = config.datasources[dsName];
if (!ds) {
throw new Error('Unknown Datasource: ' + q.datasource);
}
return {
...q,
datasourceId: ds.id,
intervalMs,
maxDataPoints,
orgId,
};
});
const req: Promise<DataQueryResponse> = getBackendSrv()
.post('/api/ds/query', {
from: range.from.valueOf().toString(),
to: range.to.valueOf().toString(),
queries: queries,
range,
expressionCount,
})
.then((rsp: any) => {
return this.toDataQueryResponse(rsp);
});
return from(req);
}
/**
* This makes the arrow libary loading async.
*/
async toDataQueryResponse(rsp: any): Promise<DataQueryResponse> {
const { resultsToDataFrames } = await import(
/* webpackChunkName: "apache-arrow-util" */ '@grafana/data/src/dataframe/ArrowDataFrame'
);
return { data: resultsToDataFrames(rsp) };
}
testDatasource() {
return Promise.resolve({});
}
newQuery(): ExpressionQuery {
return {
refId: '--', // Replaced with query
......@@ -87,7 +24,9 @@ export class ExpressionDatasourceApi extends DataSourceApi<ExpressionQuery> {
}
}
// MATCHES the constant in DataSourceWithBackend
export const ExpressionDatasourceID = '__expr__';
export const expressionDatasource = new ExpressionDatasourceApi({
id: -100,
name: ExpressionDatasourceID,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment