Commit 71a01a10 by Ryan McKinley Committed by GitHub

Chore: Convert internal grafana datasource from angular to react (#27870)

parent 26f86ba8
import { SelectableValue } from '@grafana/data';
import { GrafanaAnnotationType } from './types';
export const annotationTypes: Array<SelectableValue<GrafanaAnnotationType>> = [
{ text: 'Dashboard', value: GrafanaAnnotationType.Dashboard },
{ text: 'Tags', value: GrafanaAnnotationType.Tags },
];
export class GrafanaAnnotationsQueryCtrl {
annotation: any;
types = annotationTypes;
constructor() {
this.annotation.type = this.annotation.type || GrafanaAnnotationType.Tags;
this.annotation.limit = this.annotation.limit || 100;
}
static templateUrl = 'partials/annotations.editor.html';
}
import defaults from 'lodash/defaults';
import React, { PureComponent } from 'react';
import { InlineField, Select } from '@grafana/ui';
import { QueryEditorProps, SelectableValue } from '@grafana/data';
import { GrafanaDatasource } from '../datasource';
import { defaultQuery, GrafanaQuery, GrafanaQueryType } from '../types';
type Props = QueryEditorProps<GrafanaDatasource, GrafanaQuery>;
export class QueryEditor extends PureComponent<Props> {
queryTypes: Array<SelectableValue<GrafanaQueryType>> = [
{
label: 'Random Walk',
value: GrafanaQueryType.RandomWalk,
description: 'Random signal within the selected time rage',
},
];
onQueryTypeChange = (sel: SelectableValue<GrafanaQueryType>) => {
const { onChange, query, onRunQuery } = this.props;
onChange({ ...query, queryType: sel.value! });
onRunQuery();
};
render() {
const query = defaults(this.props.query, defaultQuery);
return (
<div className="gf-form">
<InlineField label="Query type" grow={true}>
<Select
options={this.queryTypes}
value={this.queryTypes.find(v => v.value === query.queryType) || this.queryTypes[0]}
onChange={this.onQueryTypeChange}
/>
</InlineField>
</div>
);
}
}
import { DataSourceInstanceSettings, dateTime } from '@grafana/data';
import { DataSourceInstanceSettings, dateTime, AnnotationQueryRequest } from '@grafana/data';
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
import { GrafanaDatasource } from '../datasource';
import { GrafanaDatasource } from './datasource';
import { GrafanaQuery, GrafanaAnnotationQuery, GrafanaAnnotationType } from './types';
jest.mock('@grafana/runtime', () => ({
...((jest.requireActual('@grafana/runtime') as unknown) as object),
getBackendSrv: () => backendSrv,
}));
jest.mock('app/features/templating/template_srv', () => ({
replace: (val: string) => {
return val.replace('$var2', 'replaced__delimiter__replaced2').replace('$var', 'replaced');
},
getTemplateSrv: () => ({
replace: (val: string) => {
return val.replace('$var2', 'replaced__delimiter__replaced2').replace('$var', 'replaced');
},
}),
}));
describe('grafana data source', () => {
......@@ -61,7 +61,7 @@ describe('grafana data source', () => {
describe('with type dashboard', () => {
const options = setupAnnotationQueryOptions(
{
type: 'dashboard',
type: GrafanaAnnotationType.Dashboard,
tags: ['tag1'],
},
{ id: 1 }
......@@ -78,8 +78,8 @@ describe('grafana data source', () => {
});
});
function setupAnnotationQueryOptions(annotation: { tags: string[]; type?: string }, dashboard?: { id: number }) {
return {
function setupAnnotationQueryOptions(annotation: Partial<GrafanaAnnotationQuery>, dashboard?: { id: number }) {
return ({
annotation,
dashboard,
range: {
......@@ -87,5 +87,5 @@ function setupAnnotationQueryOptions(annotation: { tags: string[]; type?: string
to: dateTime(1432288401),
},
rangeRaw: { from: 'now-24h', to: 'now' },
};
} as unknown) as AnnotationQueryRequest<GrafanaQuery>;
}
import _ from 'lodash';
import { getBackendSrv } from '@grafana/runtime';
import { DataSourceApi, DataSourceInstanceSettings } from '@grafana/data';
import {
AnnotationEvent,
AnnotationQueryRequest,
DataQueryRequest,
DataQueryResponse,
DataSourceApi,
DataSourceInstanceSettings,
} from '@grafana/data';
import templateSrv from 'app/features/templating/template_srv';
import { GrafanaQuery, GrafanaAnnotationQuery, GrafanaAnnotationType } from './types';
import { getBackendSrv, getTemplateSrv, toDataQueryResponse } from '@grafana/runtime';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
class GrafanaDatasource extends DataSourceApi<any> {
/** @ngInject */
export class GrafanaDatasource extends DataSourceApi<GrafanaQuery> {
constructor(instanceSettings: DataSourceInstanceSettings) {
super(instanceSettings);
}
query(options: any) {
return getBackendSrv()
.get('/api/tsdb/testdata/random-walk', {
from: options.range.from.valueOf(),
to: options.range.to.valueOf(),
intervalMs: options.intervalMs,
maxDataPoints: options.maxDataPoints,
})
.then((res: any) => {
const data: any[] = [];
query(request: DataQueryRequest<GrafanaQuery>): Observable<DataQueryResponse> {
const { intervalMs, maxDataPoints, range, requestId } = request;
if (res.results) {
_.forEach(res.results, queryRes => {
for (const series of queryRes.series) {
data.push({
target: series.name,
datapoints: series.points,
});
}
});
}
// Yes, this implementaiton ignores multiple targets! But that matches exisitng behavior
const params: Record<string, any> = {
intervalMs,
maxDataPoints,
from: range.from.valueOf(),
to: range.to.valueOf(),
};
return { data: data };
});
return getBackendSrv()
.fetch({
url: '/api/tsdb/testdata/random-walk',
method: 'GET',
params,
requestId,
})
.pipe(
map((rsp: any) => {
return toDataQueryResponse(rsp);
}),
catchError(err => {
return of(toDataQueryResponse(err));
})
);
}
metricFindQuery(options: any) {
return Promise.resolve([]);
}
annotationQuery(options: any) {
annotationQuery(options: AnnotationQueryRequest<GrafanaQuery>): Promise<AnnotationEvent[]> {
const templateSrv = getTemplateSrv();
const annotation = (options.annotation as unknown) as GrafanaAnnotationQuery;
const params: any = {
from: options.range.from.valueOf(),
to: options.range.to.valueOf(),
limit: options.annotation.limit,
tags: options.annotation.tags,
matchAny: options.annotation.matchAny,
limit: annotation.limit,
tags: annotation.tags,
matchAny: annotation.matchAny,
};
if (options.annotation.type === 'dashboard') {
if (annotation.type === GrafanaAnnotationType.Dashboard) {
// if no dashboard id yet return
if (!options.dashboard.id) {
return Promise.resolve([]);
......@@ -60,7 +71,7 @@ class GrafanaDatasource extends DataSourceApi<any> {
delete params.tags;
} else {
// require at least one tag
if (!_.isArray(options.annotation.tags) || options.annotation.tags.length === 0) {
if (!Array.isArray(annotation.tags) || annotation.tags.length === 0) {
return Promise.resolve([]);
}
const delimiter = '__delimiter__';
......@@ -83,7 +94,7 @@ class GrafanaDatasource extends DataSourceApi<any> {
return getBackendSrv().get(
'/api/annotations',
params,
`grafana-data-source-annotations-${options.annotation.name}-${options.dashboard?.id}`
`grafana-data-source-annotations-${annotation.name}-${options.dashboard?.id}`
);
}
......@@ -91,5 +102,3 @@ class GrafanaDatasource extends DataSourceApi<any> {
return Promise.resolve();
}
}
export { GrafanaDatasource };
import { DataSourcePlugin } from '@grafana/data';
import { GrafanaDatasource } from './datasource';
import { QueryCtrl } from 'app/plugins/sdk';
import { QueryEditor } from './components/QueryEditor';
import { GrafanaQuery } from './types';
import { GrafanaAnnotationsQueryCtrl } from './annotation_ctrl';
class GrafanaQueryCtrl extends QueryCtrl {
static templateUrl = 'partials/query.editor.html';
}
class GrafanaAnnotationsQueryCtrl {
annotation: any;
types = [
{ text: 'Dashboard', value: 'dashboard' },
{ text: 'Tags', value: 'tags' },
];
constructor() {
this.annotation.type = this.annotation.type || 'tags';
this.annotation.limit = this.annotation.limit || 100;
}
static templateUrl = 'partials/annotations.editor.html';
}
export {
GrafanaDatasource,
GrafanaDatasource as Datasource,
GrafanaQueryCtrl as QueryCtrl,
GrafanaAnnotationsQueryCtrl as AnnotationsQueryCtrl,
};
export const plugin = new DataSourcePlugin<GrafanaDatasource, GrafanaQuery>(GrafanaDatasource)
.setQueryEditor(QueryEditor)
.setAnnotationQueryCtrl(GrafanaAnnotationsQueryCtrl);
<query-editor-row query-ctrl="ctrl" can-collapse="false">
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label">Test data: random walk</label>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
</query-editor-row>
import { AnnotationQuery, DataQuery } from '@grafana/data';
//----------------------------------------------
// Query
//----------------------------------------------
export enum GrafanaQueryType {
RandomWalk = 'randomWalk',
RandomStream = 'randomStream',
HostMetrics = 'hostmetrics',
}
export interface GrafanaQuery extends DataQuery {
queryType: GrafanaQueryType; // RandomWalk by default
}
export const defaultQuery: GrafanaQuery = {
refId: 'A',
queryType: GrafanaQueryType.RandomWalk,
};
//----------------------------------------------
// Annotations
//----------------------------------------------
export enum GrafanaAnnotationType {
Dashboard = 'dashboard',
Tags = 'tags',
}
export interface GrafanaAnnotationQuery extends AnnotationQuery<GrafanaQuery> {
type: GrafanaAnnotationType; // tags
limit: number; // 100
tags?: string[];
matchAny?: boolean; // By default Grafana only shows annotations that match all tags in the query. Enabling this returns annotations that match any of the tags in the query.
}
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