TestData: Query variable support (nested + glob queries) (#18413)
* TestData: added support for nested data source variable queries, and test dashboard * Added drilldown dashboards * Fixed typescript issue
This commit is contained in:
+2
-2
@@ -11,7 +11,7 @@ import { SelectableValue } from '@grafana/data';
|
||||
|
||||
// Types
|
||||
import { QueryEditorProps } from '@grafana/ui';
|
||||
import { TestDataDatasource } from './datasource';
|
||||
import { TestDataDataSource } from './datasource';
|
||||
import { TestDataQuery, Scenario } from './types';
|
||||
|
||||
interface State {
|
||||
@@ -19,7 +19,7 @@ interface State {
|
||||
current: Scenario | null;
|
||||
}
|
||||
|
||||
type Props = QueryEditorProps<TestDataDatasource, TestDataQuery>;
|
||||
type Props = QueryEditorProps<TestDataDataSource, TestDataQuery>;
|
||||
|
||||
export class QueryEditor extends PureComponent<Props> {
|
||||
backendSrv = getBackendSrv();
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@ import React, { PureComponent } from 'react';
|
||||
|
||||
// Types
|
||||
import { PluginConfigPageProps, DataSourcePlugin } from '@grafana/ui';
|
||||
import { TestDataDatasource } from './datasource';
|
||||
import { TestDataDataSource } from './datasource';
|
||||
|
||||
interface Props extends PluginConfigPageProps<DataSourcePlugin<TestDataDatasource>> {}
|
||||
interface Props extends PluginConfigPageProps<DataSourcePlugin<TestDataDataSource>> {}
|
||||
|
||||
export class TestInfoTab extends PureComponent<Props> {
|
||||
constructor(props: Props) {
|
||||
|
||||
+22
-9
@@ -1,10 +1,17 @@
|
||||
import _ from 'lodash';
|
||||
import { DataSourceApi, DataQueryRequest, DataSourceInstanceSettings, DataStreamObserver } from '@grafana/ui';
|
||||
|
||||
import {
|
||||
DataSourceApi,
|
||||
DataQueryRequest,
|
||||
DataSourceInstanceSettings,
|
||||
DataStreamObserver,
|
||||
MetricFindValue,
|
||||
} from '@grafana/ui';
|
||||
import { TableData, TimeSeries } from '@grafana/data';
|
||||
import { TestDataQuery, Scenario } from './types';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { StreamHandler } from './StreamHandler';
|
||||
import { queryMetricTree } from './metricTree';
|
||||
import templateSrv from 'app/features/templating/template_srv';
|
||||
|
||||
type TestData = TimeSeries | TableData;
|
||||
|
||||
@@ -12,7 +19,7 @@ export interface TestDataRegistry {
|
||||
[key: string]: TestData[];
|
||||
}
|
||||
|
||||
export class TestDataDatasource extends DataSourceApi<TestDataQuery> {
|
||||
export class TestDataDataSource extends DataSourceApi<TestDataQuery> {
|
||||
streams = new StreamHandler();
|
||||
|
||||
/** @ngInject */
|
||||
@@ -23,15 +30,11 @@ export class TestDataDatasource extends DataSourceApi<TestDataQuery> {
|
||||
query(options: DataQueryRequest<TestDataQuery>, observer: DataStreamObserver) {
|
||||
const queries = options.targets.map(item => {
|
||||
return {
|
||||
refId: item.refId,
|
||||
scenarioId: item.scenarioId,
|
||||
...item,
|
||||
intervalMs: options.intervalMs,
|
||||
maxDataPoints: options.maxDataPoints,
|
||||
datasourceId: this.id,
|
||||
stringInput: item.stringInput,
|
||||
points: item.points,
|
||||
alias: item.alias,
|
||||
...item,
|
||||
alias: templateSrv.replace(item.alias || ''),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -122,4 +125,14 @@ export class TestDataDatasource extends DataSourceApi<TestDataQuery> {
|
||||
getScenarios(): Promise<Scenario[]> {
|
||||
return getBackendSrv().get('/api/tsdb/testdata/scenarios');
|
||||
}
|
||||
|
||||
metricFindQuery(query: string) {
|
||||
return new Promise<MetricFindValue[]>((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
const children = queryMetricTree(templateSrv.replace(query));
|
||||
const items = children.map(item => ({ value: item.name, text: item.name }));
|
||||
resolve(items);
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { queryMetricTree } from './metricTree';
|
||||
|
||||
describe('MetricTree', () => {
|
||||
it('queryMetric tree return right tree nodes', () => {
|
||||
const nodes = queryMetricTree('*');
|
||||
expect(nodes[0].children[0].name).toBe('AA');
|
||||
expect(nodes[0].children[1].name).toBe('AB');
|
||||
});
|
||||
|
||||
it('queryMetric tree return right tree nodes', () => {
|
||||
const nodes = queryMetricTree('A.AB.ABC.*');
|
||||
expect(nodes[0].name).toBe('ABCA');
|
||||
});
|
||||
|
||||
it('queryMetric tree supports glob paths', () => {
|
||||
const nodes = queryMetricTree('A.{AB,AC}.*').map(i => i.name);
|
||||
expect(nodes).toEqual(['ABA', 'ABB', 'ABC', 'ACA', 'ACB', 'ACC']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
export interface TreeNode {
|
||||
name: string;
|
||||
children: TreeNode[];
|
||||
}
|
||||
|
||||
/*
|
||||
* Builds a nested tree like
|
||||
* [
|
||||
* {
|
||||
* name: 'A',
|
||||
* children: [
|
||||
* { name: 'AA', children: [] },
|
||||
* { name: 'AB', children: [] },
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
function buildMetricTree(parent: string, depth: number): TreeNode[] {
|
||||
const chars = ['A', 'B', 'C'];
|
||||
const children: TreeNode[] = [];
|
||||
|
||||
if (depth > 3) {
|
||||
return [];
|
||||
}
|
||||
|
||||
for (const letter of chars) {
|
||||
const nodeName = `${parent}${letter}`;
|
||||
children.push({
|
||||
name: nodeName,
|
||||
children: buildMetricTree(nodeName, depth + 1),
|
||||
});
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
function queryTree(children: TreeNode[], query: string[], queryIndex: number): TreeNode[] {
|
||||
if (query[queryIndex] === '*') {
|
||||
return children;
|
||||
}
|
||||
|
||||
const nodeQuery = query[queryIndex];
|
||||
let result: TreeNode[] = [];
|
||||
let namesToMatch = [nodeQuery];
|
||||
|
||||
// handle glob queries
|
||||
if (nodeQuery.startsWith('{')) {
|
||||
namesToMatch = nodeQuery.replace(/\{|\}/g, '').split(',');
|
||||
}
|
||||
|
||||
for (const node of children) {
|
||||
for (const nameToMatch of namesToMatch) {
|
||||
if (node.name === nameToMatch) {
|
||||
result = result.concat(queryTree(node.children, query, queryIndex + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function queryMetricTree(query: string): TreeNode[] {
|
||||
const children = buildMetricTree('', 0);
|
||||
return queryTree(children, query.split('.'), 0);
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { DataSourcePlugin } from '@grafana/ui';
|
||||
import { TestDataDatasource } from './datasource';
|
||||
import { TestDataDataSource } from './datasource';
|
||||
import { TestDataQueryCtrl } from './query_ctrl';
|
||||
import { TestInfoTab } from './TestInfoTab';
|
||||
import { ConfigEditor } from './ConfigEditor';
|
||||
@@ -10,7 +10,7 @@ class TestDataAnnotationsQueryCtrl {
|
||||
static template = '<h2>Annotation scenario</h2>';
|
||||
}
|
||||
|
||||
export const plugin = new DataSourcePlugin(TestDataDatasource)
|
||||
export const plugin = new DataSourcePlugin(TestDataDataSource)
|
||||
.setConfigEditor(ConfigEditor)
|
||||
.setQueryCtrl(TestDataQueryCtrl)
|
||||
.setAnnotationQueryCtrl(TestDataAnnotationsQueryCtrl)
|
||||
|
||||
Reference in New Issue
Block a user