From a0d43de76195008f88131286b78c4e0fc981d98e Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Tue, 14 Jan 2020 03:59:48 -0800 Subject: [PATCH] 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 --- packages/grafana-runtime/src/index.ts | 2 + .../src/utils/DataSourceWithBackend.ts | 87 +++++++++++++++++++ public/app/core/reducers/root.test.ts | 1 + .../expressions/ExpressionDatasource.ts | 71 ++------------- 4 files changed, 95 insertions(+), 66 deletions(-) create mode 100644 packages/grafana-runtime/src/utils/DataSourceWithBackend.ts diff --git a/packages/grafana-runtime/src/index.ts b/packages/grafana-runtime/src/index.ts index e752f49ebc5..95520e1985d 100644 --- a/packages/grafana-runtime/src/index.ts +++ b/packages/grafana-runtime/src/index.ts @@ -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'; diff --git a/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts b/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts new file mode 100644 index 00000000000..12a180dcf8f --- /dev/null +++ b/packages/grafana-runtime/src/utils/DataSourceWithBackend.ts @@ -0,0 +1,87 @@ +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 { + constructor(instanceSettings: DataSourceInstanceSettings) { + super(instanceSettings); + } + + /** + * Ideally final -- any other implementation would be wrong! + */ + query(request: DataQueryRequest): Observable { + 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 = 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 { + 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({}); + } +} diff --git a/public/app/core/reducers/root.test.ts b/public/app/core/reducers/root.test.ts index 8469cbd3116..590231296d4 100644 --- a/public/app/core/reducers/root.test.ts +++ b/public/app/core/reducers/root.test.ts @@ -14,6 +14,7 @@ jest.mock('@grafana/runtime', () => ({ user: {}, }, }, + DataSourceWithBackend: jest.fn(), })); describe('recursiveCleanState', () => { diff --git a/public/app/features/expressions/ExpressionDatasource.ts b/public/app/features/expressions/ExpressionDatasource.ts index 6044451fe56..d1dd83c10ac 100644 --- a/public/app/features/expressions/ExpressionDatasource.ts +++ b/public/app/features/expressions/ExpressionDatasource.ts @@ -1,20 +1,12 @@ -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 { +export class ExpressionDatasourceApi extends DataSourceWithBackend { constructor(instanceSettings: DataSourceInstanceSettings) { super(instanceSettings); } @@ -23,61 +15,6 @@ export class ExpressionDatasourceApi extends DataSourceApi { return `Expression: ${query.type}`; } - query(request: DataQueryRequest): Observable { - 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 = 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 { - 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 { } } +// MATCHES the constant in DataSourceWithBackend export const ExpressionDatasourceID = '__expr__'; + export const expressionDatasource = new ExpressionDatasourceApi({ id: -100, name: ExpressionDatasourceID,