feat(ui): introduce flux query variiable UI

pull/5697/head
Pavel Zavora 2021-03-11 07:50:41 +01:00
parent 8caa39f0d4
commit 64ef09557d
4 changed files with 211 additions and 0 deletions

View File

@ -46,6 +46,10 @@
font-family: $code-font;
}
textarea.temp-builder--flux {
height: 100px;
}
@keyframes pulse {
0% {
color: $g19-ghost;

View File

@ -0,0 +1,136 @@
import React, {PureComponent, ChangeEvent} from 'react'
import _ from 'lodash'
import {getDeep} from 'src/utils/wrappers'
import {ErrorHandling} from 'src/shared/decorators/errors'
import TemplateFluxScriptPreview from './TemplateFluxQueryPreview'
import {hydrateTemplate} from 'src/tempVars/utils/graph'
import {TemplateBuilderProps, RemoteDataState} from 'src/types'
const DEBOUNCE_DELAY = 750
interface State {
fluxScriptInput: string // bound to input
fluxScript: string // debounced view of fluxScriptInput
fluxScriptResultsStatus: RemoteDataState
}
@ErrorHandling
class FluxQueryTemplateBuilder extends PureComponent<
TemplateBuilderProps,
State
> {
private handleFluxScriptChange: () => void = _.debounce(() => {
const {fluxScript, fluxScriptInput} = this.state
if (fluxScript === fluxScriptInput) {
return
}
this.setState({fluxScript: fluxScriptInput}, this.executeQuery)
}, DEBOUNCE_DELAY)
constructor(props: TemplateBuilderProps) {
super(props)
const fluxScript = getDeep<string>(props.template, 'query.influxql', '')
this.state = {
fluxScript,
fluxScriptInput: fluxScript,
fluxScriptResultsStatus: RemoteDataState.NotStarted,
}
}
public componentDidMount() {
this.executeQuery()
}
public render() {
const {fluxScriptInput} = this.state
return (
<>
<div className="form-group col-xs-12">
<label>Flux Script</label>
<div className="temp-builder--mq-controls ">
<textarea
className="form-control input-sm temp-builder--flux"
value={fluxScriptInput}
onChange={this.handleFluxScriptInputChange}
onBlur={this.handleFluxScriptChange}
/>
</div>
</div>
{this.renderResults()}
</>
)
}
private renderResults() {
const {template, onUpdateDefaultTemplateValue, source} = this.props
const {fluxScriptResultsStatus} = this.state
if (!source.links.flux) {
return (
<div className="form-group col-xs-12 temp-builder--results">
<p className="temp-builder--validation error">
The current source does not support flux.
</p>
</div>
)
}
return (
<TemplateFluxScriptPreview
items={template.values}
loadingStatus={fluxScriptResultsStatus}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
)
}
private handleFluxScriptInputChange = (
e: ChangeEvent<HTMLTextAreaElement>
) => {
this.setState({fluxScriptInput: e.target.value})
this.handleFluxScriptChange()
}
private executeQuery = async (): Promise<void> => {
const {template, templates, source, onUpdateTemplate} = this.props
const {fluxScript} = this.state
if (fluxScript === '' || !source.links.flux) {
return
}
this.setState({fluxScriptResultsStatus: RemoteDataState.Loading})
try {
const templateWithQuery = {
...template,
query: {influxql: fluxScript},
}
const nextTemplate = await hydrateTemplate(templateWithQuery, templates, {
proxyUrl: source.links.flux,
})
this.setState({fluxScriptResultsStatus: RemoteDataState.Done})
if (nextTemplate.values[0]) {
nextTemplate.values[0].selected = true
}
onUpdateTemplate(nextTemplate)
} catch {
this.setState({
fluxScriptResultsStatus: RemoteDataState.Error,
})
}
}
}
export default FluxQueryTemplateBuilder

View File

@ -0,0 +1,69 @@
import React, {PureComponent} from 'react'
import {ErrorHandling} from 'src/shared/decorators/errors'
import TemplatePreviewList from 'src/tempVars/components/TemplatePreviewList'
import {RemoteDataState, TemplateValue} from 'src/types'
interface Props {
items: TemplateValue[]
loadingStatus: RemoteDataState
onUpdateDefaultTemplateValue: (item: TemplateValue) => void
}
@ErrorHandling
class TemplateFluxQueryPreview extends PureComponent<Props> {
public render() {
const {items, loadingStatus, onUpdateDefaultTemplateValue} = this.props
if (loadingStatus === RemoteDataState.NotStarted) {
return null
}
if (loadingStatus === RemoteDataState.Loading) {
return (
<div className="form-group col-xs-12 temp-builder--results">
<p className="temp-builder--validation loading">
Loading Flux Query preview...
</p>
</div>
)
}
if (loadingStatus === RemoteDataState.Error) {
return (
<div className="form-group col-xs-12 temp-builder--results">
<p className="temp-builder--validation error">
Flux Query failed to execute
</p>
</div>
)
}
if (items.length === 0) {
return (
<div className="form-group col-xs-12 temp-builder--results">
<p className="temp-builder--validation warning">
Flux Query is syntactically correct but returned no results
</p>
</div>
)
}
const pluralizer = items.length === 1 ? '' : 's'
return (
<div className="form-group col-xs-12 temp-builder--results">
<p className="temp-builder--validation">
Flux Query returned <strong>{items.length}</strong> value{pluralizer}
</p>
<TemplatePreviewList
items={items}
onUpdateDefaultTemplateValue={onUpdateDefaultTemplateValue}
/>
</div>
)
}
}
export default TemplateFluxQueryPreview

View File

@ -57,6 +57,7 @@ import {
RESERVED_TEMPLATE_NAMES,
} from 'src/tempVars/constants'
import {FIVE_SECONDS} from 'src/shared/constants/index'
import FluxQueryTemplateBuilder from './FluxQueryTemplateBuilder'
interface Props {
// We will assume we are creating a new template if none is passed in
@ -90,6 +91,7 @@ const TEMPLATE_BUILDERS = {
[TemplateType.TagKeys]: TagKeysTemplateBuilder,
[TemplateType.TagValues]: TagValuesTemplateBuilder,
[TemplateType.MetaQuery]: MetaQueryTemplateBuilder,
[TemplateType.FluxQuery]: FluxQueryTemplateBuilder,
[TemplateType.Text]: TextTemplateBuilder,
}