|
|
|
@ -8,7 +8,12 @@ import { |
|
|
|
|
AdHocVariableModel, |
|
|
|
|
TypedVariableModel, |
|
|
|
|
} from '@grafana/data'; |
|
|
|
|
import { getDataSourceSrv, setTemplateSrv, TemplateSrv as BaseTemplateSrv } from '@grafana/runtime'; |
|
|
|
|
import { |
|
|
|
|
getDataSourceSrv, |
|
|
|
|
setTemplateSrv, |
|
|
|
|
TemplateSrv as BaseTemplateSrv, |
|
|
|
|
VariableInterpolation, |
|
|
|
|
} from '@grafana/runtime'; |
|
|
|
|
import { sceneGraph, FormatRegistryID, formatRegistry, VariableCustomFormatterFn } from '@grafana/scenes'; |
|
|
|
|
|
|
|
|
|
import { variableAdapters } from '../variables/adapters'; |
|
|
|
@ -23,6 +28,11 @@ interface FieldAccessorCache { |
|
|
|
|
[key: string]: (obj: any) => any; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Internal regex replace function |
|
|
|
|
*/ |
|
|
|
|
type ReplaceFunction = (fullMatch: string, variableName: string, fieldPath: string, format: string) => string; |
|
|
|
|
|
|
|
|
|
export interface TemplateSrvDependencies { |
|
|
|
|
getFilteredVariables: typeof getFilteredVariables; |
|
|
|
|
getVariables: typeof getVariables; |
|
|
|
@ -219,9 +229,8 @@ export class TemplateSrv implements BaseTemplateSrv { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
str = escape(str); |
|
|
|
|
this.regex.lastIndex = 0; |
|
|
|
|
return str.replace(this.regex, (match, var1, var2, fmt2, var3) => { |
|
|
|
|
if (this.getVariableAtIndex(var1 || var2 || var3)) { |
|
|
|
|
return this._replaceWithVariableRegex(str, undefined, (match, variableName) => { |
|
|
|
|
if (this.getVariableAtIndex(variableName)) { |
|
|
|
|
return '<span class="template-variable">' + match + '</span>'; |
|
|
|
|
} |
|
|
|
|
return match; |
|
|
|
@ -275,7 +284,12 @@ export class TemplateSrv implements BaseTemplateSrv { |
|
|
|
|
return value; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
replace(target?: string, scopedVars?: ScopedVars, format?: string | Function): string { |
|
|
|
|
replace( |
|
|
|
|
target?: string, |
|
|
|
|
scopedVars?: ScopedVars, |
|
|
|
|
format?: string | Function, |
|
|
|
|
interpolations?: VariableInterpolation[] |
|
|
|
|
): string { |
|
|
|
|
if (scopedVars && scopedVars.__sceneObject) { |
|
|
|
|
return sceneGraph.interpolate( |
|
|
|
|
scopedVars.__sceneObject.value, |
|
|
|
@ -291,63 +305,90 @@ export class TemplateSrv implements BaseTemplateSrv { |
|
|
|
|
|
|
|
|
|
this.regex.lastIndex = 0; |
|
|
|
|
|
|
|
|
|
return target.replace(this.regex, (match, var1, var2, fmt2, var3, fieldPath, fmt3) => { |
|
|
|
|
const variableName = var1 || var2 || var3; |
|
|
|
|
const variable = this.getVariableAtIndex(variableName); |
|
|
|
|
let fmt = fmt2 || fmt3 || format; |
|
|
|
|
return this._replaceWithVariableRegex(target, format, (match, variableName, fieldPath, fmt) => { |
|
|
|
|
const value = this._evaluateVariableExpression(match, variableName, fieldPath, fmt, scopedVars); |
|
|
|
|
|
|
|
|
|
if (scopedVars) { |
|
|
|
|
const value = this.getVariableValue(variableName, fieldPath, scopedVars); |
|
|
|
|
const text = this.getVariableText(variableName, value, scopedVars); |
|
|
|
|
// If we get passed this interpolations map we will also record all the expressions that were replaced
|
|
|
|
|
if (interpolations) { |
|
|
|
|
interpolations.push({ match, variableName, fieldPath, format: fmt, value, found: value !== match }); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (value !== null && value !== undefined) { |
|
|
|
|
if (scopedVars[variableName].skipFormat) { |
|
|
|
|
fmt = undefined; |
|
|
|
|
} |
|
|
|
|
return value; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return this.formatValue(value, fmt, variable, text); |
|
|
|
|
private _evaluateVariableExpression( |
|
|
|
|
match: string, |
|
|
|
|
variableName: string, |
|
|
|
|
fieldPath: string, |
|
|
|
|
format: string | Function | undefined, |
|
|
|
|
scopedVars: ScopedVars | undefined |
|
|
|
|
) { |
|
|
|
|
const variable = this.getVariableAtIndex(variableName); |
|
|
|
|
|
|
|
|
|
if (scopedVars) { |
|
|
|
|
const value = this.getVariableValue(variableName, fieldPath, scopedVars); |
|
|
|
|
const text = this.getVariableText(variableName, value, scopedVars); |
|
|
|
|
|
|
|
|
|
if (value !== null && value !== undefined) { |
|
|
|
|
if (scopedVars[variableName]?.skipFormat) { |
|
|
|
|
format = undefined; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!variable) { |
|
|
|
|
return match; |
|
|
|
|
return this.formatValue(value, format, variable, text); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (fmt === FormatRegistryID.queryParam || isAdHoc(variable)) { |
|
|
|
|
const value = variableAdapters.get(variable.type).getValueForUrl(variable); |
|
|
|
|
const text = isAdHoc(variable) ? variable.id : variable.current.text; |
|
|
|
|
if (!variable) { |
|
|
|
|
return match; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return this.formatValue(value, fmt, variable, text); |
|
|
|
|
} |
|
|
|
|
if (format === FormatRegistryID.queryParam || isAdHoc(variable)) { |
|
|
|
|
const value = variableAdapters.get(variable.type).getValueForUrl(variable); |
|
|
|
|
const text = isAdHoc(variable) ? variable.id : variable.current.text; |
|
|
|
|
|
|
|
|
|
const systemValue = this.grafanaVariables.get(variable.current.value); |
|
|
|
|
if (systemValue) { |
|
|
|
|
return this.formatValue(systemValue, fmt, variable); |
|
|
|
|
} |
|
|
|
|
return this.formatValue(value, format, variable, text); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let value = variable.current.value; |
|
|
|
|
let text = variable.current.text; |
|
|
|
|
const systemValue = this.grafanaVariables.get(variable.current.value); |
|
|
|
|
if (systemValue) { |
|
|
|
|
return this.formatValue(systemValue, format, variable); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (this.isAllValue(value)) { |
|
|
|
|
value = this.getAllValue(variable); |
|
|
|
|
text = ALL_VARIABLE_TEXT; |
|
|
|
|
// skip formatting of custom all values unless format set to text or percentencode
|
|
|
|
|
if (variable.allValue && fmt !== FormatRegistryID.text && fmt !== FormatRegistryID.percentEncode) { |
|
|
|
|
return this.replace(value); |
|
|
|
|
} |
|
|
|
|
let value = variable.current.value; |
|
|
|
|
let text = variable.current.text; |
|
|
|
|
|
|
|
|
|
if (this.isAllValue(value)) { |
|
|
|
|
value = this.getAllValue(variable); |
|
|
|
|
text = ALL_VARIABLE_TEXT; |
|
|
|
|
// skip formatting of custom all values unless format set to text or percentencode
|
|
|
|
|
if (variable.allValue && format !== FormatRegistryID.text && format !== FormatRegistryID.percentEncode) { |
|
|
|
|
return this.replace(value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (fieldPath) { |
|
|
|
|
const fieldValue = this.getVariableValue(variableName, fieldPath, { |
|
|
|
|
[variableName]: { value, text }, |
|
|
|
|
}); |
|
|
|
|
if (fieldValue !== null && fieldValue !== undefined) { |
|
|
|
|
return this.formatValue(fieldValue, fmt, variable, text); |
|
|
|
|
} |
|
|
|
|
if (fieldPath) { |
|
|
|
|
const fieldValue = this.getVariableValue(variableName, fieldPath, { |
|
|
|
|
[variableName]: { value, text }, |
|
|
|
|
}); |
|
|
|
|
if (fieldValue !== null && fieldValue !== undefined) { |
|
|
|
|
return this.formatValue(fieldValue, format, variable, text); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const res = this.formatValue(value, fmt, variable, text); |
|
|
|
|
return res; |
|
|
|
|
return this.formatValue(value, format, variable, text); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Tries to unify the different variable format capture groups into a simpler replacer function |
|
|
|
|
*/ |
|
|
|
|
private _replaceWithVariableRegex(text: string, format: string | Function | undefined, replace: ReplaceFunction) { |
|
|
|
|
this.regex.lastIndex = 0; |
|
|
|
|
|
|
|
|
|
return text.replace(this.regex, (match, var1, var2, fmt2, var3, fieldPath, fmt3) => { |
|
|
|
|
const variableName = var1 || var2 || var3; |
|
|
|
|
const fmt = fmt2 || fmt3 || format; |
|
|
|
|
return replace(match, variableName, fieldPath, fmt); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|