feat(ui): introduce flux query variiable UI
parent
8caa39f0d4
commit
64ef09557d
|
@ -46,6 +46,10 @@
|
|||
font-family: $code-font;
|
||||
}
|
||||
|
||||
textarea.temp-builder--flux {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
color: $g19-ghost;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue