Merge pull request #3734 from influxdata/tempVars/meta-queries
Add "Custom Meta Query" template variable typepull/10616/head
commit
c81658f938
|
@ -159,7 +159,7 @@ type Range struct {
|
||||||
// TemplateValue is a value use to replace a template in an InfluxQL query
|
// TemplateValue is a value use to replace a template in an InfluxQL query
|
||||||
type TemplateValue struct {
|
type TemplateValue struct {
|
||||||
Value string `json:"value"` // Value is the specific value used to replace a template in an InfluxQL query
|
Value string `json:"value"` // Value is the specific value used to replace a template in an InfluxQL query
|
||||||
Type string `json:"type"` // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant
|
Type string `json:"type"` // Type can be tagKey, tagValue, fieldKey, csv, measurement, database, constant, influxql
|
||||||
Selected bool `json:"selected"` // Selected states that this variable has been picked to use for replacement
|
Selected bool `json:"selected"` // Selected states that this variable has been picked to use for replacement
|
||||||
Key string `json:"key,omitempty"` // Key is the key for the Value if the Template Type is 'map'
|
Key string `json:"key,omitempty"` // Key is the key for the Value if the Template Type is 'map'
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ type TemplateID string
|
||||||
type Template struct {
|
type Template struct {
|
||||||
TemplateVar
|
TemplateVar
|
||||||
ID TemplateID `json:"id"` // ID is the unique ID associated with this template
|
ID TemplateID `json:"id"` // ID is the unique ID associated with this template
|
||||||
Type string `json:"type"` // Type can be fieldKeys, tagKeys, tagValues, CSV, constant, query, measurements, databases, map
|
Type string `json:"type"` // Type can be fieldKeys, tagKeys, tagValues, CSV, constant, measurements, databases, map, influxql
|
||||||
Label string `json:"label"` // Label is a user-facing description of the Template
|
Label string `json:"label"` // Label is a user-facing description of the Template
|
||||||
Query *TemplateQuery `json:"query,omitempty"` // Query is used to generate the choices for a template
|
Query *TemplateQuery `json:"query,omitempty"` // Query is used to generate the choices for a template
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ func RenderTemplate(query string, t chronograf.TemplateVar, now time.Time) (stri
|
||||||
return strings.Replace(q, t.Var, `"`+t.Values[0].Value+`"`, -1), nil
|
return strings.Replace(q, t.Var, `"`+t.Values[0].Value+`"`, -1), nil
|
||||||
case "tagValue", "timeStamp":
|
case "tagValue", "timeStamp":
|
||||||
return strings.Replace(q, t.Var, `'`+t.Values[0].Value+`'`, -1), nil
|
return strings.Replace(q, t.Var, `'`+t.Values[0].Value+`'`, -1), nil
|
||||||
case "csv", "constant":
|
case "csv", "constant", "influxql":
|
||||||
return strings.Replace(q, t.Var, t.Values[0].Value, -1), nil
|
return strings.Replace(q, t.Var, t.Values[0].Value, -1), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,14 @@ func ValidTemplateRequest(template *chronograf.Template) error {
|
||||||
switch template.Type {
|
switch template.Type {
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unknown template type %s", template.Type)
|
return fmt.Errorf("Unknown template type %s", template.Type)
|
||||||
case "query", "constant", "csv", "fieldKeys", "tagKeys", "tagValues", "measurements", "databases", "map":
|
case "constant", "csv", "fieldKeys", "tagKeys", "tagValues", "measurements", "databases", "map", "influxql":
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range template.Values {
|
for _, v := range template.Values {
|
||||||
switch v.Type {
|
switch v.Type {
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unknown template variable type %s", v.Type)
|
return fmt.Errorf("Unknown template variable type %s", v.Type)
|
||||||
case "csv", "fieldKey", "tagKey", "tagValue", "measurement", "database", "constant":
|
case "csv", "fieldKey", "tagKey", "tagValue", "measurement", "database", "constant", "influxql":
|
||||||
}
|
}
|
||||||
|
|
||||||
if template.Type == "map" && v.Key == "" {
|
if template.Type == "map" && v.Key == "" {
|
||||||
|
@ -30,8 +30,8 @@ func ValidTemplateRequest(template *chronograf.Template) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if template.Type == "query" && template.Query == nil {
|
if template.Type == "influxql" && template.Query == nil {
|
||||||
return fmt.Errorf("No query set for template of type 'query'")
|
return fmt.Errorf("No query set for template of type 'influxql'")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -57,7 +57,7 @@ func TestValidTemplateRequest(t *testing.T) {
|
||||||
name: "No query set",
|
name: "No query set",
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
template: &chronograf.Template{
|
template: &chronograf.Template{
|
||||||
Type: "query",
|
Type: "influxql",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,7 +5,9 @@ import {replace} from 'react-router-redux'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import queryString from 'query-string'
|
import queryString from 'query-string'
|
||||||
|
|
||||||
|
import {proxy} from 'src/utils/queryUrlGenerator'
|
||||||
import {isUserAuthorized, EDITOR_ROLE} from 'src/auth/Authorized'
|
import {isUserAuthorized, EDITOR_ROLE} from 'src/auth/Authorized'
|
||||||
|
import {parseMetaQuery} from 'src/tempVars/utils/parsing'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getDashboards as getDashboardsAJAX,
|
getDashboards as getDashboardsAJAX,
|
||||||
|
@ -15,7 +17,6 @@ import {
|
||||||
updateDashboardCell as updateDashboardCellAJAX,
|
updateDashboardCell as updateDashboardCellAJAX,
|
||||||
addDashboardCell as addDashboardCellAJAX,
|
addDashboardCell as addDashboardCellAJAX,
|
||||||
deleteDashboardCell as deleteDashboardCellAJAX,
|
deleteDashboardCell as deleteDashboardCellAJAX,
|
||||||
getTempVarValuesBySourceQuery,
|
|
||||||
createDashboard as createDashboardAJAX,
|
createDashboard as createDashboardAJAX,
|
||||||
} from 'src/dashboards/apis'
|
} from 'src/dashboards/apis'
|
||||||
import {getMe} from 'src/shared/apis/auth'
|
import {getMe} from 'src/shared/apis/auth'
|
||||||
|
@ -48,7 +49,6 @@ import {
|
||||||
} from 'src/shared/copy/notifications'
|
} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
import {makeQueryForTemplate} from 'src/dashboards/utils/tempVars'
|
import {makeQueryForTemplate} from 'src/dashboards/utils/tempVars'
|
||||||
import parsers from 'src/shared/parsing'
|
|
||||||
import {getDeep} from 'src/utils/wrappers'
|
import {getDeep} from 'src/utils/wrappers'
|
||||||
|
|
||||||
import idNormalizer, {TYPE_ID} from 'src/normalizers/id'
|
import idNormalizer, {TYPE_ID} from 'src/normalizers/id'
|
||||||
|
@ -628,25 +628,23 @@ export const hydrateTempVarValuesAsync = (
|
||||||
const dashboard = getState().dashboardUI.dashboards.find(
|
const dashboard = getState().dashboardUI.dashboards.find(
|
||||||
d => d.id === dashboardID
|
d => d.id === dashboardID
|
||||||
)
|
)
|
||||||
|
const templates: Template[] = dashboard.templates
|
||||||
|
const queries = templates
|
||||||
|
.filter(
|
||||||
|
template => getDeep<string>(template, 'query.influxql', '') !== ''
|
||||||
|
)
|
||||||
|
.map(async template => {
|
||||||
|
const query = makeQueryForTemplate(template.query)
|
||||||
|
const response = await proxy({source: source.links.proxy, query})
|
||||||
|
const values = parseMetaQuery(query, response.data)
|
||||||
|
|
||||||
const tempsWithQueries = dashboard.templates.filter(
|
return {template, values}
|
||||||
({query}) => !!query.influxql
|
|
||||||
)
|
|
||||||
|
|
||||||
const asyncQueries = tempsWithQueries.map(({query}) =>
|
|
||||||
getTempVarValuesBySourceQuery(source, {
|
|
||||||
query: makeQueryForTemplate(query),
|
|
||||||
})
|
})
|
||||||
)
|
const results = await Promise.all(queries)
|
||||||
|
|
||||||
const results = await Promise.all(asyncQueries)
|
for (const {template, values} of results) {
|
||||||
|
dispatch(editTemplateVariableValues(+dashboard.id, template.id, values))
|
||||||
results.forEach(({data}, i) => {
|
}
|
||||||
const {type, query, id} = tempsWithQueries[i]
|
|
||||||
const parsed = parsers[type](data, query.tagKey || query.measurement)
|
|
||||||
const vals = parsed[type]
|
|
||||||
dispatch(editTemplateVariableValues(+dashboard.id, id, vals))
|
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
dispatch(errorThrown(error))
|
dispatch(errorThrown(error))
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import AJAX from 'utils/ajax'
|
import AJAX from 'utils/ajax'
|
||||||
import {proxy} from 'utils/queryUrlGenerator'
|
|
||||||
|
|
||||||
export function getDashboards() {
|
export function getDashboards() {
|
||||||
return AJAX({
|
return AJAX({
|
||||||
|
@ -98,19 +97,3 @@ export const editTemplateVariables = async templateVariable => {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTempVarValuesBySourceQuery = async (source, templateQuery) => {
|
|
||||||
const {
|
|
||||||
query,
|
|
||||||
db,
|
|
||||||
// rp, TODO
|
|
||||||
tempVars,
|
|
||||||
} = templateQuery
|
|
||||||
try {
|
|
||||||
// TODO: add rp as argument to proxy
|
|
||||||
return await proxy({source: source.links.proxy, query, db, tempVars})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import {getDeep} from 'src/utils/wrappers'
|
||||||
|
|
||||||
|
interface ParseShowSeriesResponse {
|
||||||
|
errors: string[]
|
||||||
|
series: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseShowSeries = (response): ParseShowSeriesResponse => {
|
||||||
|
const results = response.results[0]
|
||||||
|
|
||||||
|
if (results.error) {
|
||||||
|
return {errors: [results.error], series: []}
|
||||||
|
}
|
||||||
|
|
||||||
|
const seriesValues = getDeep<string[]>(results, 'series.0.values', [])
|
||||||
|
|
||||||
|
if (!seriesValues.length) {
|
||||||
|
return {errors: [], series: []}
|
||||||
|
}
|
||||||
|
|
||||||
|
const series = seriesValues.map(s => s[0])
|
||||||
|
|
||||||
|
return {series, errors: []}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default parseShowSeries
|
|
@ -0,0 +1,169 @@
|
||||||
|
import React, {PureComponent, ChangeEvent} from 'react'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
import {proxy} from 'src/utils/queryUrlGenerator'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
import TemplateMetaQueryPreview from 'src/tempVars/components/TemplateMetaQueryPreview'
|
||||||
|
import {parseMetaQuery, isInvalidMetaQuery} from 'src/tempVars/utils/parsing'
|
||||||
|
import {getDeep} from 'src/utils/wrappers'
|
||||||
|
|
||||||
|
import {
|
||||||
|
TemplateBuilderProps,
|
||||||
|
RemoteDataState,
|
||||||
|
TemplateValueType,
|
||||||
|
} from 'src/types'
|
||||||
|
|
||||||
|
const DEBOUNCE_DELAY = 750
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
metaQueryInput: string // bound to input
|
||||||
|
metaQuery: string // debounced view of metaQueryInput
|
||||||
|
metaQueryResults: string[]
|
||||||
|
metaQueryResultsStatus: RemoteDataState
|
||||||
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class CustomMetaQueryTemplateBuilder extends PureComponent<
|
||||||
|
TemplateBuilderProps,
|
||||||
|
State
|
||||||
|
> {
|
||||||
|
private handleMetaQueryChange: () => void = _.debounce(() => {
|
||||||
|
const {metaQuery, metaQueryInput} = this.state
|
||||||
|
|
||||||
|
if (metaQuery === metaQueryInput) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({metaQuery: metaQueryInput}, this.executeQuery)
|
||||||
|
}, DEBOUNCE_DELAY)
|
||||||
|
|
||||||
|
constructor(props: TemplateBuilderProps) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
const metaQuery = getDeep<string>(props.template, 'query.influxql', '')
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
metaQuery,
|
||||||
|
metaQueryInput: metaQuery,
|
||||||
|
metaQueryResults: [],
|
||||||
|
metaQueryResultsStatus: RemoteDataState.NotStarted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
this.executeQuery()
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {metaQueryInput} = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="temp-builder csv-temp-builder">
|
||||||
|
<div className="form-group">
|
||||||
|
<label>Meta Query</label>
|
||||||
|
<div className="temp-builder--mq-controls">
|
||||||
|
<textarea
|
||||||
|
className="form-control"
|
||||||
|
value={metaQueryInput}
|
||||||
|
onChange={this.handleMetaQueryInputChange}
|
||||||
|
onBlur={this.handleMetaQueryChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{this.renderResults()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderResults() {
|
||||||
|
const {metaQueryResults, metaQueryResultsStatus} = this.state
|
||||||
|
|
||||||
|
if (this.showInvalidMetaQueryMessage) {
|
||||||
|
return (
|
||||||
|
<div className="temp-builder-results">
|
||||||
|
<p className="error">Meta Query is not valid.</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TemplateMetaQueryPreview
|
||||||
|
items={metaQueryResults}
|
||||||
|
loadingStatus={metaQueryResultsStatus}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get showInvalidMetaQueryMessage(): boolean {
|
||||||
|
const {metaQuery} = this.state
|
||||||
|
|
||||||
|
return this.isInvalidMetaQuery && metaQuery !== ''
|
||||||
|
}
|
||||||
|
|
||||||
|
private get isInvalidMetaQuery(): boolean {
|
||||||
|
const {metaQuery} = this.state
|
||||||
|
|
||||||
|
return isInvalidMetaQuery(metaQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleMetaQueryInputChange = (
|
||||||
|
e: ChangeEvent<HTMLTextAreaElement>
|
||||||
|
) => {
|
||||||
|
this.setState({metaQueryInput: e.target.value})
|
||||||
|
this.handleMetaQueryChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
private executeQuery = async (): Promise<void> => {
|
||||||
|
const {template, source, onUpdateTemplate} = this.props
|
||||||
|
const {metaQuery} = this.state
|
||||||
|
|
||||||
|
if (this.isInvalidMetaQuery) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({metaQueryResultsStatus: RemoteDataState.Loading})
|
||||||
|
|
||||||
|
try {
|
||||||
|
const {data} = await proxy({
|
||||||
|
source: source.links.proxy,
|
||||||
|
query: metaQuery,
|
||||||
|
})
|
||||||
|
|
||||||
|
const metaQueryResults = parseMetaQuery(metaQuery, data)
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
metaQueryResults,
|
||||||
|
metaQueryResultsStatus: RemoteDataState.Done,
|
||||||
|
})
|
||||||
|
|
||||||
|
const nextValues = metaQueryResults.map(result => {
|
||||||
|
return {
|
||||||
|
type: TemplateValueType.MetaQuery,
|
||||||
|
value: result,
|
||||||
|
selected: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (nextValues[0]) {
|
||||||
|
nextValues[0].selected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextTemplate = {
|
||||||
|
...template,
|
||||||
|
values: nextValues,
|
||||||
|
query: {
|
||||||
|
influxql: metaQuery,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateTemplate(nextTemplate)
|
||||||
|
} catch {
|
||||||
|
this.setState({
|
||||||
|
metaQueryResults: [],
|
||||||
|
metaQueryResultsStatus: RemoteDataState.Error,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomMetaQueryTemplateBuilder
|
|
@ -18,6 +18,7 @@ import MeasurementsTemplateBuilder from 'src/tempVars/components/MeasurementsTem
|
||||||
import FieldKeysTemplateBuilder from 'src/tempVars/components/FieldKeysTemplateBuilder'
|
import FieldKeysTemplateBuilder from 'src/tempVars/components/FieldKeysTemplateBuilder'
|
||||||
import TagKeysTemplateBuilder from 'src/tempVars/components/TagKeysTemplateBuilder'
|
import TagKeysTemplateBuilder from 'src/tempVars/components/TagKeysTemplateBuilder'
|
||||||
import TagValuesTemplateBuilder from 'src/tempVars/components/TagValuesTemplateBuilder'
|
import TagValuesTemplateBuilder from 'src/tempVars/components/TagValuesTemplateBuilder'
|
||||||
|
import MetaQueryTemplateBuilder from 'src/tempVars/components/MetaQueryTemplateBuilder'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Template,
|
Template,
|
||||||
|
@ -59,6 +60,7 @@ const TEMPLATE_BUILDERS = {
|
||||||
[TemplateType.FieldKeys]: FieldKeysTemplateBuilder,
|
[TemplateType.FieldKeys]: FieldKeysTemplateBuilder,
|
||||||
[TemplateType.TagKeys]: TagKeysTemplateBuilder,
|
[TemplateType.TagKeys]: TagKeysTemplateBuilder,
|
||||||
[TemplateType.TagValues]: TagValuesTemplateBuilder,
|
[TemplateType.TagValues]: TagValuesTemplateBuilder,
|
||||||
|
[TemplateType.MetaQuery]: MetaQueryTemplateBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatName = name => `:${name.replace(/:/g, '')}:`
|
const formatName = name => `:${name.replace(/:/g, '')}:`
|
||||||
|
|
|
@ -34,6 +34,10 @@ export const TEMPLATE_TYPES_LIST: TemplateTypesListItem[] = [
|
||||||
text: 'CSV',
|
text: 'CSV',
|
||||||
type: TemplateType.CSV,
|
type: TemplateType.CSV,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: 'Custom Meta Query',
|
||||||
|
type: TemplateType.MetaQuery,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const TEMPLATE_VARIABLE_TYPES = {
|
export const TEMPLATE_VARIABLE_TYPES = {
|
||||||
|
@ -43,6 +47,7 @@ export const TEMPLATE_VARIABLE_TYPES = {
|
||||||
[TemplateType.FieldKeys]: TemplateValueType.FieldKey,
|
[TemplateType.FieldKeys]: TemplateValueType.FieldKey,
|
||||||
[TemplateType.TagKeys]: TemplateValueType.TagKey,
|
[TemplateType.TagKeys]: TemplateValueType.TagKey,
|
||||||
[TemplateType.TagValues]: TemplateValueType.TagValue,
|
[TemplateType.TagValues]: TemplateValueType.TagValue,
|
||||||
|
[TemplateType.MetaQuery]: TemplateValueType.MetaQuery,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TEMPLATE_VARIABLE_QUERIES = {
|
export const TEMPLATE_VARIABLE_QUERIES = {
|
||||||
|
@ -136,6 +141,18 @@ export const DEFAULT_TEMPLATES: DefaultTemplates = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
[TemplateType.MetaQuery]: () => {
|
||||||
|
return {
|
||||||
|
id: uuid.v4(),
|
||||||
|
tempVar: ':my-meta-query:',
|
||||||
|
values: [],
|
||||||
|
type: TemplateType.MetaQuery,
|
||||||
|
label: '',
|
||||||
|
query: {
|
||||||
|
influxql: '',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RESERVED_TEMPLATE_NAMES = [
|
export const RESERVED_TEMPLATE_NAMES = [
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import parseShowDatabases from 'src/shared/parsing/showDatabases'
|
||||||
|
import parseShowFieldKeys from 'src/shared/parsing/showFieldKeys'
|
||||||
|
import parseShowTagKeys from 'src/shared/parsing/showTagKeys'
|
||||||
|
import parseShowTagValues from 'src/shared/parsing/showTagValues'
|
||||||
|
import parseShowMeasurements from 'src/shared/parsing/showMeasurements'
|
||||||
|
import parseShowSeries from 'src/shared/parsing/showSeries'
|
||||||
|
|
||||||
|
export const parseMetaQuery = (metaQuery: string, response): string[] => {
|
||||||
|
const metaQueryStart = getMetaQueryPrefix(metaQuery)
|
||||||
|
|
||||||
|
if (!metaQueryStart) {
|
||||||
|
throw new Error('Could not find parser for meta query')
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = PARSERS[metaQueryStart]
|
||||||
|
const extractor = EXTRACTORS[metaQueryStart]
|
||||||
|
const parsed = parser(response)
|
||||||
|
|
||||||
|
if (parsed.errors.length) {
|
||||||
|
throw new Error(parsed.errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractor(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isInvalidMetaQuery = (metaQuery: string): boolean =>
|
||||||
|
!getMetaQueryPrefix(metaQuery)
|
||||||
|
|
||||||
|
const getMetaQueryPrefix = (metaQuery: string): string | null => {
|
||||||
|
const words = metaQuery
|
||||||
|
.trim()
|
||||||
|
.toUpperCase()
|
||||||
|
.split(' ')
|
||||||
|
const firstTwoWords = words.slice(0, 2).join(' ')
|
||||||
|
const firstThreeWords = words.slice(0, 3).join(' ')
|
||||||
|
|
||||||
|
return VALID_META_QUERY_PREFIXES.find(
|
||||||
|
q => q === firstTwoWords || q === firstThreeWords
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const VALID_META_QUERY_PREFIXES = [
|
||||||
|
'SHOW DATABASES',
|
||||||
|
'SHOW MEASUREMENTS',
|
||||||
|
'SHOW SERIES',
|
||||||
|
'SHOW TAG VALUES',
|
||||||
|
'SHOW FIELD KEYS',
|
||||||
|
'SHOW TAG KEYS',
|
||||||
|
]
|
||||||
|
|
||||||
|
const PARSERS = {
|
||||||
|
'SHOW DATABASES': parseShowDatabases,
|
||||||
|
'SHOW FIELD KEYS': parseShowFieldKeys,
|
||||||
|
'SHOW MEASUREMENTS': parseShowMeasurements,
|
||||||
|
'SHOW SERIES': parseShowSeries,
|
||||||
|
'SHOW TAG VALUES': parseShowTagValues,
|
||||||
|
'SHOW TAG KEYS': parseShowTagKeys,
|
||||||
|
}
|
||||||
|
|
||||||
|
const EXTRACTORS = {
|
||||||
|
'SHOW DATABASES': parsed => parsed.databases,
|
||||||
|
'SHOW FIELD KEYS': parsed => {
|
||||||
|
const {fieldSets} = parsed
|
||||||
|
const fieldSetsValues = Object.values(fieldSets) as string[]
|
||||||
|
|
||||||
|
return fieldSetsValues.reduce((acc, current) => [...acc, ...current], [])
|
||||||
|
},
|
||||||
|
'SHOW MEASUREMENTS': parsed => {
|
||||||
|
const {measurementSets} = parsed
|
||||||
|
|
||||||
|
return measurementSets.reduce(
|
||||||
|
(acc, current) => [...acc, ...current.measurements],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
'SHOW TAG KEYS': parsed => parsed.tagKeys,
|
||||||
|
'SHOW TAG VALUES': parsed => {
|
||||||
|
const {tags} = parsed
|
||||||
|
const tagsValues = Object.values(tags) as string[]
|
||||||
|
|
||||||
|
return tagsValues.reduce((acc, current) => [...acc, ...current], [])
|
||||||
|
},
|
||||||
|
'SHOW SERIES': parsed => parsed.series,
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ export enum TemplateValueType {
|
||||||
CSV = 'csv',
|
CSV = 'csv',
|
||||||
Points = 'points',
|
Points = 'points',
|
||||||
Constant = 'constant',
|
Constant = 'constant',
|
||||||
|
MetaQuery = 'influxql',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TemplateValue {
|
export interface TemplateValue {
|
||||||
|
@ -34,8 +35,8 @@ export enum TemplateType {
|
||||||
TagKeys = 'tagKeys',
|
TagKeys = 'tagKeys',
|
||||||
TagValues = 'tagValues',
|
TagValues = 'tagValues',
|
||||||
CSV = 'csv',
|
CSV = 'csv',
|
||||||
Query = 'query',
|
|
||||||
Databases = 'databases',
|
Databases = 'databases',
|
||||||
|
MetaQuery = 'influxql',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Template {
|
export interface Template {
|
||||||
|
|
Loading…
Reference in New Issue