Refactor template variable resolution

Now handles the `Text` case properly.
bugfix/regex-tempvar-queries
Christopher Henn 2018-07-20 09:54:32 -07:00 committed by Chris Henn
parent 099abd329c
commit eb258cf314
4 changed files with 490 additions and 427 deletions

View File

@ -5,11 +5,9 @@ import templateReplace, {
templateInternalReplace,
} from 'src/tempVars/utils/replace'
import {getSelectedValue, getLocalSelectedValue} from 'src/tempVars/utils'
import {resolveValues} from 'src/tempVars/utils'
import {TEMPLATE_VARIABLE_TYPES} from 'src/tempVars/constants'
import {Template, TemplateType, TemplateValue, RemoteDataState} from 'src/types'
import {Template, RemoteDataState} from 'src/types'
type TemplateName = string
@ -223,97 +221,23 @@ export async function hydrateTemplate(
selections = {},
}: HydrateTemplateOptions
): Promise<Template> {
const selection = selections[template.tempVar]
let values
let templateValues
let newValues: string[]
if (template.query && template.query.influxql) {
const query = templateReplace(templateInternalReplace(template), templates)
const renderedQuery = templateReplace(
templateInternalReplace(template),
templates
)
values = await fetcher.fetch(query)
templateValues = newTemplateValues(template, values, selection)
} else {
templateValues = newConstantTemplateValues(template, selection)
newValues = await fetcher.fetch(renderedQuery)
}
const selection = selections[template.tempVar]
const templateValues = resolveValues(template, newValues, selection)
return {...template, values: templateValues}
}
export function newTemplateValues(
template: Template,
newValues: string[],
hopefullySelectedValue?: string
): TemplateValue[] {
if (!newValues.length) {
return []
}
const type = TEMPLATE_VARIABLE_TYPES[template.type]
let selectedValue = getSelectedValue(template)
if (!selectedValue || !newValues.includes(selectedValue)) {
// The persisted selected value may no longer exist as a result for the
// templates metaquery. In this case we select the first actual result
selectedValue = newValues[0]
}
let localSelectedValue = hopefullySelectedValue
if (!localSelectedValue) {
localSelectedValue = getLocalSelectedValue(template)
}
if (!localSelectedValue || !newValues.includes(localSelectedValue)) {
localSelectedValue = selectedValue
}
return newValues.map(value => {
return {
type,
value,
selected: value === selectedValue,
localSelected: value === localSelectedValue,
}
})
}
export function newConstantTemplateValues(
template: Template,
hopefullySelectedValue?: string
): TemplateValue[] {
if (!template.values.length) {
return []
}
let selectedValue = template.values.find(v => v.selected)
if (!selectedValue) {
selectedValue = template.values[0]
}
let localSelectedValue = template.values.find(v => {
return template.type === TemplateType.Map
? v.key === hopefullySelectedValue
: v.value === hopefullySelectedValue
})
if (!localSelectedValue) {
localSelectedValue = template.values.find(v => v.localSelected)
}
if (!localSelectedValue) {
localSelectedValue = selectedValue
}
return template.values.map(v => ({
...v,
selected: v.value === selectedValue.value,
localSelected: v.value === localSelectedValue.value,
}))
}
export async function hydrateTemplates(
templates: Template[],
hydrateOptions: HydrateTemplateOptions

View File

@ -1,4 +1,10 @@
import {Template} from 'src/types'
import {TEMPLATE_VARIABLE_TYPES} from 'src/tempVars/constants'
import {
Template,
TemplateValue,
TemplateType,
TemplateValueType,
} from 'src/types'
export const trimAndRemoveQuotes = elt => {
const trimmed = elt.trim()
@ -10,7 +16,135 @@ export const trimAndRemoveQuotes = elt => {
export const formatTempVar = name =>
`:${name.replace(/:/g, '').replace(/\s/g, '')}:`
export const getSelectedValue = (template: Template): string | null => {
export const resolveValues = (
template: Template,
newValues?: string[],
hopefullySelectedValue?: string
): TemplateValue[] => {
switch (template.type) {
case TemplateType.Text:
return newTemplateValueText(template, hopefullySelectedValue)
case TemplateType.CSV:
case TemplateType.Map:
return newTemplateValueConstant(template, hopefullySelectedValue)
case TemplateType.MetaQuery:
case TemplateType.FieldKeys:
case TemplateType.Measurements:
case TemplateType.TagKeys:
case TemplateType.TagValues:
case TemplateType.Databases:
return newTemplateValueQuery(template, newValues, hopefullySelectedValue)
default:
throw new Error(
`TemplateValue resolution for TemplateType ${
template.type
} not implemented`
)
}
}
const newTemplateValueQuery = (
template: Template,
newValues: string[],
hopefullySelectedValue?: string
) => {
if (!newValues.length) {
return []
}
const type = TEMPLATE_VARIABLE_TYPES[template.type]
let selectedValue = getSelectedValue(template)
if (!selectedValue || !newValues.includes(selectedValue)) {
// The persisted selected value may no longer exist as a result for the
// templates metaquery. In this case we select the first actual result
selectedValue = newValues[0]
}
let localSelectedValue = hopefullySelectedValue
if (!localSelectedValue) {
localSelectedValue = getLocalSelectedValue(template)
}
if (!localSelectedValue || !newValues.includes(localSelectedValue)) {
localSelectedValue = selectedValue
}
return newValues.map(value => {
return {
type,
value,
selected: value === selectedValue,
localSelected: value === localSelectedValue,
}
})
}
const newTemplateValueConstant = (
template: Template,
hopefullySelectedValue?: string
) => {
if (!template.values.length) {
return []
}
let selectedValue = template.values.find(v => v.selected)
if (!selectedValue) {
selectedValue = template.values[0]
}
let localSelectedValue = template.values.find(v => {
return template.type === TemplateType.Map
? v.key === hopefullySelectedValue
: v.value === hopefullySelectedValue
})
if (!localSelectedValue) {
localSelectedValue = template.values.find(v => v.localSelected)
}
if (!localSelectedValue) {
localSelectedValue = selectedValue
}
return template.values.map(v => ({
...v,
selected: v.value === selectedValue.value,
localSelected: v.value === localSelectedValue.value,
}))
}
const newTemplateValueText = (
template: Template,
hopefullySelectedValue?: string
) => {
if (!!hopefullySelectedValue) {
return [
{
value: hopefullySelectedValue,
type: TemplateValueType.Constant,
localSelected: true,
selected: false,
},
]
} else if (template.values.length) {
return [template.values[0]]
} else {
return [
{
value: '',
type: TemplateValueType.Constant,
localSelected: true,
selected: false,
},
]
}
}
const getSelectedValue = (template: Template): string | null => {
const selected = template.values.find(v => v.selected)
if (selected) {
@ -20,7 +154,7 @@ export const getSelectedValue = (template: Template): string | null => {
return null
}
export const getLocalSelectedValue = (template: Template): string | null => {
const getLocalSelectedValue = (template: Template): string | null => {
const selected = template.values.find(v => v.localSelected)
if (selected) {

View File

@ -2,8 +2,6 @@ import {
getDependencyNames,
graphFromTemplates,
hydrateTemplates,
newTemplateValues,
newConstantTemplateValues,
topologicalSort,
} from 'src/tempVars/utils/graph'
@ -315,341 +313,6 @@ describe('graphFromTemplates', () => {
})
})
describe('newTemplateValues', () => {
test('uses supplied value for localSelectedValue if exists', () => {
const template = {
id: '0',
tempVar: ':a:',
label: '',
query: {},
type: TemplateType.CSV,
values: [
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: false,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
],
}
const result = newTemplateValues(template, ['a', 'b', 'c'], 'c')
expect(result).toEqual([
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: false,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: true,
},
])
})
test('uses existing localSelectedValue if none supplied', () => {
const template = {
id: '0',
tempVar: ':a:',
label: '',
query: {},
type: TemplateType.CSV,
values: [
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: true,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
],
}
const result = newTemplateValues(template, ['a', 'b', 'c'])
expect(result).toEqual([
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: true,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
])
})
test('defaults to selected value if no localSelectedValue and no supplied value', () => {
const template = {
id: '0',
tempVar: ':a:',
label: '',
query: {},
type: TemplateType.CSV,
values: [
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: false,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
],
}
const result = newTemplateValues(template, ['a', 'b', 'c'])
expect(result).toEqual([
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: true,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
])
})
})
describe('newConstantTemplateValues', () => {
test('maintains current selections for Map template variables', () => {
const template = {
id: '',
label: '',
query: {},
tempVar: ':a:',
type: TemplateType.Map,
values: [
{
type: TemplateValueType.Map,
key: 'k1',
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.Map,
key: 'k2',
value: 'v2',
selected: false,
localSelected: true,
},
],
}
const expected = [
{
type: TemplateValueType.Map,
key: 'k1',
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.Map,
key: 'k2',
value: 'v2',
selected: false,
localSelected: true,
},
]
expect(newConstantTemplateValues(template)).toEqual(expected)
})
test('maintains current selections for CSV template variables', () => {
const template = {
id: '',
label: '',
query: {},
tempVar: ':a:',
type: TemplateType.CSV,
values: [
{
type: TemplateValueType.CSV,
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.CSV,
value: 'v2',
selected: false,
localSelected: true,
},
],
}
const expected = [
{
type: TemplateValueType.CSV,
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.CSV,
value: 'v2',
selected: false,
localSelected: true,
},
]
expect(newConstantTemplateValues(template)).toEqual(expected)
})
test('applies a selected value for Map template variables', () => {
const template = {
id: '',
label: '',
query: {},
tempVar: ':a:',
type: TemplateType.Map,
values: [
{
type: TemplateValueType.Map,
key: 'k1',
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.Map,
key: 'k2',
value: 'v2',
selected: false,
localSelected: true,
},
],
}
const expected = [
{
type: TemplateValueType.Map,
key: 'k1',
value: 'v1',
selected: true,
localSelected: true,
},
{
type: TemplateValueType.Map,
key: 'k2',
value: 'v2',
selected: false,
localSelected: false,
},
]
expect(newConstantTemplateValues(template, 'k1')).toEqual(expected)
})
test('applies a selected value for CSV template variables', () => {
const template = {
id: '',
label: '',
query: {},
tempVar: ':a:',
type: TemplateType.CSV,
values: [
{
type: TemplateValueType.CSV,
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.CSV,
value: 'v2',
selected: false,
localSelected: true,
},
],
}
const expected = [
{
type: TemplateValueType.CSV,
value: 'v1',
selected: true,
localSelected: true,
},
{
type: TemplateValueType.CSV,
value: 'v2',
selected: false,
localSelected: false,
},
]
expect(newConstantTemplateValues(template, 'v1')).toEqual(expected)
})
})
describe('hydrateTemplates', () => {
test('it can hydrate a simple template graph', async () => {
const templates = [

View File

@ -0,0 +1,342 @@
import {resolveValues} from 'src/tempVars/utils'
import {TemplateType, TemplateValueType} from 'src/types'
describe('template value resolution', () => {
describe('query backed templates', () => {
test('uses supplied value for localSelectedValue if exists', () => {
const template = {
id: '0',
tempVar: ':a:',
label: '',
query: {},
type: TemplateType.CSV,
values: [
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: false,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
],
}
const result = resolveValues(template, ['a', 'b', 'c'], 'c')
expect(result).toEqual([
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: false,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: true,
},
])
})
test('uses existing localSelectedValue if none supplied', () => {
const template = {
id: '0',
tempVar: ':a:',
label: '',
query: {},
type: TemplateType.CSV,
values: [
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: true,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
],
}
const result = resolveValues(template, ['a', 'b', 'c'])
expect(result).toEqual([
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: true,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
])
})
test('defaults to selected value if no localSelectedValue and no supplied value', () => {
const template = {
id: '0',
tempVar: ':a:',
label: '',
query: {},
type: TemplateType.CSV,
values: [
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: false,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
],
}
const result = resolveValues(template, ['a', 'b', 'c'])
expect(result).toEqual([
{
value: 'a',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
{
value: 'b',
type: TemplateValueType.CSV,
selected: true,
localSelected: true,
},
{
value: 'c',
type: TemplateValueType.CSV,
selected: false,
localSelected: false,
},
])
})
})
describe('Map templates', () => {
test('maintains current selections', () => {
const template = {
id: '',
label: '',
query: {},
tempVar: ':a:',
type: TemplateType.Map,
values: [
{
type: TemplateValueType.Map,
key: 'k1',
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.Map,
key: 'k2',
value: 'v2',
selected: false,
localSelected: true,
},
],
}
const expected = [
{
type: TemplateValueType.Map,
key: 'k1',
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.Map,
key: 'k2',
value: 'v2',
selected: false,
localSelected: true,
},
]
expect(resolveValues(template)).toEqual(expected)
})
test('uses supplied selected value when exists', () => {
const template = {
id: '',
label: '',
query: {},
tempVar: ':a:',
type: TemplateType.Map,
values: [
{
type: TemplateValueType.Map,
key: 'k1',
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.Map,
key: 'k2',
value: 'v2',
selected: false,
localSelected: true,
},
],
}
const expected = [
{
type: TemplateValueType.Map,
key: 'k1',
value: 'v1',
selected: true,
localSelected: true,
},
{
type: TemplateValueType.Map,
key: 'k2',
value: 'v2',
selected: false,
localSelected: false,
},
]
expect(resolveValues(template, null, 'k1')).toEqual(expected)
})
})
describe('CSV templates', () => {
test('maintains current selections', () => {
const template = {
id: '',
label: '',
query: {},
tempVar: ':a:',
type: TemplateType.CSV,
values: [
{
type: TemplateValueType.CSV,
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.CSV,
value: 'v2',
selected: false,
localSelected: true,
},
],
}
const expected = [
{
type: TemplateValueType.CSV,
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.CSV,
value: 'v2',
selected: false,
localSelected: true,
},
]
expect(resolveValues(template)).toEqual(expected)
})
test('uses supplied selected value when exists', () => {
const template = {
id: '',
label: '',
query: {},
tempVar: ':a:',
type: TemplateType.CSV,
values: [
{
type: TemplateValueType.CSV,
value: 'v1',
selected: true,
localSelected: false,
},
{
type: TemplateValueType.CSV,
value: 'v2',
selected: false,
localSelected: true,
},
],
}
const expected = [
{
type: TemplateValueType.CSV,
value: 'v1',
selected: true,
localSelected: true,
},
{
type: TemplateValueType.CSV,
value: 'v2',
selected: false,
localSelected: false,
},
]
expect(resolveValues(template, null, 'v1')).toEqual(expected)
})
})
})