import { cx, css } from '@emotion/css'; import React, { PureComponent } from 'react'; import { SelectableValue } from '@grafana/data'; import { getTemplateSrv } from '@grafana/runtime'; import { InlineFormLabel, LinkButton, Segment, CodeEditor, MonacoEditor, CodeEditorSuggestionItem, CodeEditorSuggestionItemKind, } from '@grafana/ui'; import InfluxDatasource from '../datasource'; import { InfluxQuery } from '../types'; type Props = { onChange: (query: InfluxQuery) => void; onRunQuery: () => void; query: InfluxQuery; // `datasource` is not used internally, but this component is used at some places // directly, where the `datasource` prop has to exist. later, when the whole // query-editor gets converted to react we can stop using this component directly // and then we can probably remove the datasource attribute. datasource: InfluxDatasource; }; const samples: Array> = [ { label: 'Show buckets', description: 'List the available buckets (table)', value: 'buckets()' }, { label: 'Simple query', description: 'filter by measurement and field', value: `from(bucket: "db/rp") |> range(start: v.timeRangeStart, stop:v.timeRangeStop) |> filter(fn: (r) => r._measurement == "example-measurement" and r._field == "example-field" )`, }, { label: 'Grouped Query', description: 'Group by (min/max/sum/median)', value: `// v.windowPeriod is a variable referring to the current optimized window period (currently: $interval) from(bucket: v.bucket) |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r["_measurement"] == "measurement1" or r["_measurement"] =~ /^.*?regex.*$/) |> filter(fn: (r) => r["_field"] == "field2" or r["_field"] =~ /^.*?regex.*$/) |> aggregateWindow(every: v.windowPeriod, fn: mean|median|max|count|derivative|sum) |> yield(name: "some-name")`, }, { label: 'Filter by value', description: 'Results between a min/max', value: `// v.bucket, v.timeRangeStart, and v.timeRange stop are all variables supported by the flux plugin and influxdb from(bucket: v.bucket) |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r["_value"] >= 10 and r["_value"] <= 20)`, }, { label: 'Schema Exploration: (measurements)', description: 'Get a list of measurement using flux', value: `import "influxdata/influxdb/v1" 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:v.timeRangeStop) |> keys() |> keep(columns: ["_value"]) |> group() |> distinct()`, }, { label: 'Schema Exploration: (tag keys)', description: 'Get a list of tag keys using flux', value: `import "influxdata/influxdb/v1" v1.tagKeys(bucket: v.bucket)`, }, { label: 'Schema Exploration: (tag values)', description: 'Get a list of tag values using flux', value: `import "influxdata/influxdb/v1" v1.tagValues( bucket: v.bucket, tag: "host", predicate: (r) => true, start: -1d )`, }, ]; export class FluxQueryEditor extends PureComponent { onFluxQueryChange = (query: string) => { this.props.onChange({ ...this.props.query, query }); this.props.onRunQuery(); }; onSampleChange = (val: SelectableValue) => { this.props.onChange({ ...this.props.query, query: val.value!, }); // Angular HACK: Since the target does not actually change! this.forceUpdate(); this.props.onRunQuery(); }; getSuggestions = (): CodeEditorSuggestionItem[] => { const sugs: CodeEditorSuggestionItem[] = [ { label: 'v.timeRangeStart', kind: CodeEditorSuggestionItemKind.Property, detail: 'The start time', }, { label: 'v.timeRangeStop', kind: CodeEditorSuggestionItemKind.Property, detail: 'The stop time', }, { label: 'v.windowPeriod', kind: CodeEditorSuggestionItemKind.Property, detail: 'based on max data points', }, { label: 'v.defaultBucket', kind: CodeEditorSuggestionItemKind.Property, detail: 'bucket configured in the datsource', }, { label: 'v.organization', kind: CodeEditorSuggestionItemKind.Property, detail: 'org configured for the datsource', }, ]; const templateSrv = getTemplateSrv(); templateSrv.getVariables().forEach((variable) => { const label = '${' + variable.name + '}'; let val = templateSrv.replace(label); if (val === label) { val = ''; } sugs.push({ label, kind: CodeEditorSuggestionItemKind.Text, detail: `(Template Variable) ${val}`, }); }); return sugs; }; // For some reason in angular, when this component gets re-mounted, the width // is not set properly. This forces the layout shortly after mount so that it // displays OK. Note: this is not an issue when used directly in react editorDidMountCallbackHack = (editor: MonacoEditor) => { setTimeout(() => editor.layout(), 100); }; render() { const { query } = this.props; const helpTooltip = (
Type: ctrl+space to show template variable suggestions
Many queries can be copied from Chronograf
); return ( <>
Flux language syntax
Help
); } }