mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
InfluxDB: support flux editor for template queries (#27370)
This commit is contained in:
parent
b867050cfb
commit
ae385983f4
@ -9,4 +9,4 @@ export * from './types';
|
||||
export { loadPluginCss, SystemJS, PluginCssOptions } from './utils/plugin';
|
||||
export { reportMetaAnalytics } from './utils/analytics';
|
||||
export { DataSourceWithBackend, HealthCheckResult, HealthStatus } from './utils/DataSourceWithBackend';
|
||||
export { toDataQueryError, toDataQueryResponse } from './utils/queryResponse';
|
||||
export { toDataQueryError, toDataQueryResponse, frameToMetricFindValue } from './utils/queryResponse';
|
||||
|
@ -8,6 +8,9 @@ import {
|
||||
TimeSeries,
|
||||
TableData,
|
||||
toDataFrame,
|
||||
DataFrame,
|
||||
MetricFindValue,
|
||||
FieldType,
|
||||
} from '@grafana/data';
|
||||
|
||||
interface DataResponse {
|
||||
@ -115,3 +118,22 @@ export function toDataQueryError(err: any): DataQueryError {
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/** Return the first string or non-time field as the value */
|
||||
export function frameToMetricFindValue(frame: DataFrame): MetricFindValue[] {
|
||||
if (!frame || !frame.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const values: MetricFindValue[] = [];
|
||||
let field = frame.fields.find(f => f.type === FieldType.string);
|
||||
if (!field) {
|
||||
field = frame.fields.find(f => f.type !== FieldType.time);
|
||||
}
|
||||
if (field) {
|
||||
for (let i = 0; i < field.values.length; i++) {
|
||||
values.push({ text: '' + field.values.get(i) });
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
@ -66,14 +66,15 @@ func getQueryModelTSDB(query *tsdb.Query, timeRange *tsdb.TimeRange, dsInfo *mod
|
||||
if model.Options.Organization == "" {
|
||||
model.Options.Organization = dsInfo.JsonData.Get("organization").MustString("")
|
||||
}
|
||||
|
||||
startTime, err := timeRange.ParseFrom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err != nil && timeRange.From != "" {
|
||||
return nil, fmt.Errorf("error reading startTime: %w", err)
|
||||
}
|
||||
|
||||
endTime, err := timeRange.ParseTo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err != nil && timeRange.To != "" {
|
||||
return nil, fmt.Errorf("error reading endTime: %w", err)
|
||||
}
|
||||
|
||||
// Copy directly from the well typed query
|
||||
|
@ -60,7 +60,7 @@ v1.measurements(bucket: v.bucket)`,
|
||||
label: 'Schema Exploration: (fields)',
|
||||
description: 'Return every possible key in a single table',
|
||||
value: `from(bucket: v.bucket)
|
||||
|> range(start: v.timeRangeStart, stop:timeRangeStop)
|
||||
|> range(start: v.timeRangeStart, stop:v.timeRangeStop)
|
||||
|> keys()
|
||||
|> keep(columns: ["_value"])
|
||||
|> group()
|
||||
|
@ -0,0 +1,47 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import InfluxDatasource from '../datasource';
|
||||
import { InlineFormLabel, TextArea } from '@grafana/ui';
|
||||
import { FluxQueryEditor } from './FluxQueryEditor';
|
||||
|
||||
interface Props {
|
||||
query: string; // before flux, it was always a string
|
||||
onChange: (query?: string) => void;
|
||||
datasource: InfluxDatasource;
|
||||
}
|
||||
|
||||
export default class VariableQueryEditor extends PureComponent<Props> {
|
||||
onRefresh = () => {
|
||||
// noop
|
||||
};
|
||||
|
||||
render() {
|
||||
let { query, datasource, onChange } = this.props;
|
||||
if (datasource.isFlux) {
|
||||
return (
|
||||
<FluxQueryEditor
|
||||
target={{
|
||||
refId: 'A',
|
||||
query,
|
||||
}}
|
||||
refresh={this.onRefresh}
|
||||
change={v => onChange(v.query)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="gf-form-inline">
|
||||
<InlineFormLabel width={10}>Query</InlineFormLabel>
|
||||
<div className="gf-form-inline gf-form--grow">
|
||||
<TextArea
|
||||
value={query || ''}
|
||||
placeholder="metric name or tags query"
|
||||
rows={1}
|
||||
className="gf-form-input"
|
||||
onChange={e => onChange(e.currentTarget.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -9,6 +9,9 @@ import {
|
||||
dateTime,
|
||||
LoadingState,
|
||||
QueryResultMeta,
|
||||
MetricFindValue,
|
||||
AnnotationQueryRequest,
|
||||
AnnotationEvent,
|
||||
} from '@grafana/data';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import InfluxSeries from './influx_series';
|
||||
@ -16,7 +19,7 @@ import InfluxQueryModel from './influx_query_model';
|
||||
import ResponseParser from './response_parser';
|
||||
import { InfluxQueryBuilder } from './query_builder';
|
||||
import { InfluxQuery, InfluxOptions, InfluxVersion } from './types';
|
||||
import { getBackendSrv, getTemplateSrv, DataSourceWithBackend } from '@grafana/runtime';
|
||||
import { getBackendSrv, getTemplateSrv, DataSourceWithBackend, frameToMetricFindValue } from '@grafana/runtime';
|
||||
import { Observable, from } from 'rxjs';
|
||||
|
||||
export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery, InfluxOptions> {
|
||||
@ -31,7 +34,7 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
|
||||
interval: any;
|
||||
responseParser: any;
|
||||
httpMode: string;
|
||||
is2x: boolean;
|
||||
isFlux: boolean;
|
||||
|
||||
constructor(instanceSettings: DataSourceInstanceSettings<InfluxOptions>) {
|
||||
super(instanceSettings);
|
||||
@ -51,11 +54,11 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
|
||||
this.interval = settingsData.timeInterval;
|
||||
this.httpMode = settingsData.httpMode || 'GET';
|
||||
this.responseParser = new ResponseParser();
|
||||
this.is2x = settingsData.version === InfluxVersion.Flux;
|
||||
this.isFlux = settingsData.version === InfluxVersion.Flux;
|
||||
}
|
||||
|
||||
query(request: DataQueryRequest<InfluxQuery>): Observable<DataQueryResponse> {
|
||||
if (this.is2x) {
|
||||
if (this.isFlux) {
|
||||
return super.query(request);
|
||||
}
|
||||
|
||||
@ -64,7 +67,7 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
|
||||
}
|
||||
|
||||
getQueryDisplayText(query: InfluxQuery) {
|
||||
if (this.is2x) {
|
||||
if (this.isFlux) {
|
||||
return query.query;
|
||||
}
|
||||
return new InfluxQueryModel(query).render(false);
|
||||
@ -177,14 +180,21 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
|
||||
});
|
||||
}
|
||||
|
||||
annotationQuery(options: any) {
|
||||
async annotationQuery(options: AnnotationQueryRequest<any>): Promise<AnnotationEvent[]> {
|
||||
if (this.isFlux) {
|
||||
return Promise.reject({
|
||||
message: 'Annotations are not yet supported with flux queries',
|
||||
});
|
||||
}
|
||||
|
||||
// InfluxQL puts a query string on the annotation
|
||||
if (!options.annotation.query) {
|
||||
return Promise.reject({
|
||||
message: 'Query missing in annotation definition',
|
||||
});
|
||||
}
|
||||
|
||||
const timeFilter = this.getTimeFilter({ rangeRaw: options.rangeRaw, timezone: options.timezone });
|
||||
const timeFilter = this.getTimeFilter({ rangeRaw: options.rangeRaw, timezone: options.dashboard.timezone });
|
||||
let query = options.annotation.query.replace('$timeFilter', timeFilter);
|
||||
query = getTemplateSrv().replace(query, undefined, 'regex');
|
||||
|
||||
@ -256,7 +266,26 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
|
||||
return expandedQueries;
|
||||
}
|
||||
|
||||
metricFindQuery(query: string, options?: any) {
|
||||
async metricFindQuery(query: string, options?: any): Promise<MetricFindValue[]> {
|
||||
if (this.isFlux) {
|
||||
const target: InfluxQuery = {
|
||||
refId: 'metricFindQuery',
|
||||
query,
|
||||
};
|
||||
return super
|
||||
.query({
|
||||
...options, // includes 'range'
|
||||
targets: [target],
|
||||
} as DataQueryRequest)
|
||||
.toPromise()
|
||||
.then(rsp => {
|
||||
if (rsp.data?.length) {
|
||||
return frameToMetricFindValue(rsp.data[0]);
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
const interpolated = getTemplateSrv().replace(query, undefined, 'regex');
|
||||
|
||||
return this._seriesQuery(interpolated, options).then(resp => {
|
||||
@ -308,7 +337,7 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
|
||||
}
|
||||
|
||||
testDatasource() {
|
||||
if (this.is2x) {
|
||||
if (this.isFlux) {
|
||||
// TODO: eventually use the real /health endpoint
|
||||
const request: DataQueryRequest<InfluxQuery> = {
|
||||
targets: [{ refId: 'test', query: 'buckets()' }],
|
||||
|
@ -3,6 +3,7 @@ import { InfluxQueryCtrl } from './query_ctrl';
|
||||
import InfluxStartPage from './components/InfluxStartPage';
|
||||
import { DataSourcePlugin } from '@grafana/data';
|
||||
import ConfigEditor from './components/ConfigEditor';
|
||||
import VariableQueryEditor from './components/VariableQueryEditor';
|
||||
|
||||
// This adds a directive that is used in the query editor
|
||||
import './components/FluxQueryEditor';
|
||||
@ -15,4 +16,5 @@ export const plugin = new DataSourcePlugin(InfluxDatasource)
|
||||
.setConfigEditor(ConfigEditor)
|
||||
.setQueryCtrl(InfluxQueryCtrl)
|
||||
.setAnnotationQueryCtrl(InfluxAnnotationsQueryCtrl)
|
||||
.setVariableQueryEditor(VariableQueryEditor)
|
||||
.setExploreStartPage(InfluxStartPage);
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
<query-editor-row ng-if="ctrl.datasource.is2x" query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="true">
|
||||
<query-editor-row ng-if="ctrl.datasource.isFlux" query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="true">
|
||||
<flux-query-editor
|
||||
target="ctrl.target"
|
||||
change="ctrl.onChange"
|
||||
@ -7,7 +7,7 @@
|
||||
></flux-query-editor>
|
||||
</query-editor-row>
|
||||
|
||||
<query-editor-row ng-if="!ctrl.datasource.is2x" query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="true">
|
||||
<query-editor-row ng-if="!ctrl.datasource.isFlux" query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="true">
|
||||
<div ng-if="ctrl.target.rawQuery">
|
||||
<div class="gf-form">
|
||||
<textarea
|
||||
|
@ -256,7 +256,7 @@ export class InfluxQueryCtrl extends QueryCtrl {
|
||||
|
||||
// Only valid for InfluxQL queries
|
||||
toggleEditorMode() {
|
||||
if (this.datasource.is2x) {
|
||||
if (this.datasource.isFlux) {
|
||||
return; // nothing
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user