mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TemplateSrv: Refactoring out all formats to a formatRegistry (#26607)
This commit is contained in:
parent
0d933b79b7
commit
cfac143244
212
public/app/features/templating/formatRegistry.ts
Normal file
212
public/app/features/templating/formatRegistry.ts
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
import { Registry, RegistryItem, VariableModel, textUtil, dateTime } from '@grafana/data';
|
||||||
|
import { map, isArray, replace } from 'lodash';
|
||||||
|
|
||||||
|
export interface FormatRegistryItem extends RegistryItem {
|
||||||
|
formatter(value: any, args: string[], variable: VariableModel): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatRegistry = new Registry<FormatRegistryItem>(() => {
|
||||||
|
const formats: FormatRegistryItem[] = [
|
||||||
|
{
|
||||||
|
id: 'lucene',
|
||||||
|
name: 'Lucene',
|
||||||
|
description: 'Values are lucene escaped and multi-valued variables generate an OR expression',
|
||||||
|
formatter: value => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return luceneEscape(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Array && value.length === 0) {
|
||||||
|
return '__empty__';
|
||||||
|
}
|
||||||
|
|
||||||
|
const quotedValues = map(value, (val: string) => {
|
||||||
|
return '"' + luceneEscape(val) + '"';
|
||||||
|
});
|
||||||
|
|
||||||
|
return '(' + quotedValues.join(' OR ') + ')';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'regex',
|
||||||
|
name: 'Regex',
|
||||||
|
description: 'Values are regex escaped and multi-valued variables generate a (<value>|<value>) expression',
|
||||||
|
formatter: value => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return kbn.regexEscape(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const escapedValues = map(value, kbn.regexEscape);
|
||||||
|
if (escapedValues.length === 1) {
|
||||||
|
return escapedValues[0];
|
||||||
|
}
|
||||||
|
return '(' + escapedValues.join('|') + ')';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'pipe',
|
||||||
|
name: 'Pipe',
|
||||||
|
description: 'Values are seperated by | character',
|
||||||
|
formatter: value => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return value.join('|');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'distributed',
|
||||||
|
name: 'Distributed',
|
||||||
|
description: 'Multiple values are formatted like variable=value',
|
||||||
|
formatter: (value, args, variable) => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = map(value, (val: any, index: number) => {
|
||||||
|
if (index !== 0) {
|
||||||
|
return variable.name + '=' + val;
|
||||||
|
} else {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return value.join(',');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'csv',
|
||||||
|
name: 'Csv',
|
||||||
|
description: 'Comma seperated values',
|
||||||
|
formatter: (value, args, variable) => {
|
||||||
|
if (isArray(value)) {
|
||||||
|
return value.join(',');
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'html',
|
||||||
|
name: 'HTML',
|
||||||
|
description: 'HTML escaping of values',
|
||||||
|
formatter: (value, args, variable) => {
|
||||||
|
if (isArray(value)) {
|
||||||
|
return textUtil.escapeHtml(value.join(', '));
|
||||||
|
}
|
||||||
|
return textUtil.escapeHtml(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'json',
|
||||||
|
name: 'JSON',
|
||||||
|
description: 'JSON stringify valu',
|
||||||
|
formatter: (value, args, variable) => {
|
||||||
|
return JSON.stringify(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'percentencode',
|
||||||
|
name: 'Percent encode',
|
||||||
|
description: 'Useful for url escaping values',
|
||||||
|
formatter: (value, args, variable) => {
|
||||||
|
// like glob, but url escaped
|
||||||
|
if (isArray(value)) {
|
||||||
|
return encodeURIComponentStrict('{' + value.join(',') + '}');
|
||||||
|
}
|
||||||
|
return encodeURIComponentStrict(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'singlequote',
|
||||||
|
name: 'Single quote',
|
||||||
|
description: 'Single quoted values',
|
||||||
|
formatter: (value, args, variable) => {
|
||||||
|
// escape single quotes with backslash
|
||||||
|
const regExp = new RegExp(`'`, 'g');
|
||||||
|
if (isArray(value)) {
|
||||||
|
return map(value, (v: string) => `'${replace(v, regExp, `\\'`)}'`).join(',');
|
||||||
|
}
|
||||||
|
return `'${replace(value, regExp, `\\'`)}'`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'doublequote',
|
||||||
|
name: 'Double quote',
|
||||||
|
description: 'Double quoted values',
|
||||||
|
formatter: (value, args, variable) => {
|
||||||
|
// escape double quotes with backslash
|
||||||
|
const regExp = new RegExp('"', 'g');
|
||||||
|
if (isArray(value)) {
|
||||||
|
return map(value, (v: string) => `"${replace(v, regExp, '\\"')}"`).join(',');
|
||||||
|
}
|
||||||
|
return `"${replace(value, regExp, '\\"')}"`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'sqlstring',
|
||||||
|
name: 'SQL string',
|
||||||
|
description: 'SQL string quoting and commas for use in IN statements and other scenarios',
|
||||||
|
formatter: (value, args, variable) => {
|
||||||
|
// escape single quotes by pairing them
|
||||||
|
const regExp = new RegExp(`'`, 'g');
|
||||||
|
if (isArray(value)) {
|
||||||
|
return map(value, v => `'${replace(v, regExp, "''")}'`).join(',');
|
||||||
|
}
|
||||||
|
return `'${replace(value, regExp, "''")}'`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'date',
|
||||||
|
name: 'Date',
|
||||||
|
description: 'Format date in different ways',
|
||||||
|
formatter: (value, args, variable) => {
|
||||||
|
const arg = args[0] ?? 'iso';
|
||||||
|
|
||||||
|
switch (arg) {
|
||||||
|
case 'ms':
|
||||||
|
return value;
|
||||||
|
case 'seconds':
|
||||||
|
return `${Math.round(parseInt(value, 10)! / 1000)}`;
|
||||||
|
case 'iso':
|
||||||
|
return dateTime(parseInt(value, 10)).toISOString();
|
||||||
|
default:
|
||||||
|
return dateTime(parseInt(value, 10)).format(arg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'glob',
|
||||||
|
name: 'Glob',
|
||||||
|
description: 'Format multi valued variables using glob syntax, example {value1,value2}',
|
||||||
|
formatter: (value, args, variable) => {
|
||||||
|
if (isArray(value) && value.length > 1) {
|
||||||
|
return '{' + value.join(',') + '}';
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return formats;
|
||||||
|
});
|
||||||
|
|
||||||
|
function luceneEscape(value: string) {
|
||||||
|
return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* encode string according to RFC 3986; in contrast to encodeURIComponent()
|
||||||
|
* also the sub-delims "!", "'", "(", ")" and "*" are encoded;
|
||||||
|
* unicode handling uses UTF-8 as in ECMA-262.
|
||||||
|
*/
|
||||||
|
function encodeURIComponentStrict(str: string) {
|
||||||
|
return encodeURIComponent(str).replace(/[!'()*]/g, c => {
|
||||||
|
return (
|
||||||
|
'%' +
|
||||||
|
c
|
||||||
|
.charCodeAt(0)
|
||||||
|
.toString(16)
|
||||||
|
.toUpperCase()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -595,11 +595,6 @@ describe('templateSrv', () => {
|
|||||||
initTemplateSrv([]);
|
initTemplateSrv([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be possible to fetch value with getBuilInIntervalValue', () => {
|
|
||||||
const val = _templateSrv.getBuiltInIntervalValue();
|
|
||||||
expect(val).toBe('1s');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should replace $__interval_ms with interval milliseconds', () => {
|
it('should replace $__interval_ms with interval milliseconds', () => {
|
||||||
const target = _templateSrv.replace('10 * $__interval_ms', {
|
const target = _templateSrv.replace('10 * $__interval_ms', {
|
||||||
__interval_ms: { text: '100', value: '100' },
|
__interval_ms: { text: '100', value: '100' },
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
import kbn from 'app/core/utils/kbn';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { deprecationWarning, ScopedVars, textUtil, TimeRange, dateTime } from '@grafana/data';
|
import { deprecationWarning, ScopedVars, TimeRange } from '@grafana/data';
|
||||||
import { getFilteredVariables, getVariables, getVariableWithName } from '../variables/state/selectors';
|
import { getFilteredVariables, getVariables, getVariableWithName } from '../variables/state/selectors';
|
||||||
import { variableRegex } from '../variables/utils';
|
import { variableRegex } from '../variables/utils';
|
||||||
import { isAdHoc } from '../variables/guard';
|
import { isAdHoc } from '../variables/guard';
|
||||||
import { VariableModel } from '../variables/types';
|
import { VariableModel } from '../variables/types';
|
||||||
import { setTemplateSrv, TemplateSrv as BaseTemplateSrv } from '@grafana/runtime';
|
import { setTemplateSrv, TemplateSrv as BaseTemplateSrv } from '@grafana/runtime';
|
||||||
import { variableAdapters } from '../variables/adapters';
|
import { variableAdapters } from '../variables/adapters';
|
||||||
|
import { formatRegistry } from './formatRegistry';
|
||||||
function luceneEscape(value: string) {
|
|
||||||
return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1');
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FieldAccessorCache {
|
interface FieldAccessorCache {
|
||||||
[key: string]: (obj: any) => any;
|
[key: string]: (obj: any) => any;
|
||||||
@ -33,13 +29,10 @@ export class TemplateSrv implements BaseTemplateSrv {
|
|||||||
private regex = variableRegex;
|
private regex = variableRegex;
|
||||||
private index: any = {};
|
private index: any = {};
|
||||||
private grafanaVariables: any = {};
|
private grafanaVariables: any = {};
|
||||||
private builtIns: any = {};
|
|
||||||
private timeRange?: TimeRange | null = null;
|
private timeRange?: TimeRange | null = null;
|
||||||
private fieldAccessorCache: FieldAccessorCache = {};
|
private fieldAccessorCache: FieldAccessorCache = {};
|
||||||
|
|
||||||
constructor(private dependencies: TemplateSrvDependencies = runtimeDependencies) {
|
constructor(private dependencies: TemplateSrvDependencies = runtimeDependencies) {
|
||||||
this.builtIns['__interval'] = { text: '1s', value: '1s' };
|
|
||||||
this.builtIns['__interval_ms'] = { text: '100', value: '100' };
|
|
||||||
this._variables = [];
|
this._variables = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,10 +42,6 @@ export class TemplateSrv implements BaseTemplateSrv {
|
|||||||
this.updateIndex();
|
this.updateIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
getBuiltInIntervalValue() {
|
|
||||||
return this.builtIns.__interval.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated: this instance variable should not be used and will be removed in future releases
|
* @deprecated: this instance variable should not be used and will be removed in future releases
|
||||||
*
|
*
|
||||||
@ -118,34 +107,6 @@ export class TemplateSrv implements BaseTemplateSrv {
|
|||||||
return filters;
|
return filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
luceneFormat(value: any) {
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
return luceneEscape(value);
|
|
||||||
}
|
|
||||||
if (value instanceof Array && value.length === 0) {
|
|
||||||
return '__empty__';
|
|
||||||
}
|
|
||||||
const quotedValues = _.map(value, val => {
|
|
||||||
return '"' + luceneEscape(val) + '"';
|
|
||||||
});
|
|
||||||
return '(' + quotedValues.join(' OR ') + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode string according to RFC 3986; in contrast to encodeURIComponent()
|
|
||||||
// also the sub-delims "!", "'", "(", ")" and "*" are encoded;
|
|
||||||
// unicode handling uses UTF-8 as in ECMA-262.
|
|
||||||
encodeURIComponentStrict(str: string) {
|
|
||||||
return encodeURIComponent(str).replace(/[!'()*]/g, c => {
|
|
||||||
return (
|
|
||||||
'%' +
|
|
||||||
c
|
|
||||||
.charCodeAt(0)
|
|
||||||
.toString(16)
|
|
||||||
.toUpperCase()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
formatValue(value: any, format: any, variable: any) {
|
formatValue(value: any, format: any, variable: any) {
|
||||||
// for some scopedVars there is no variable
|
// for some scopedVars there is no variable
|
||||||
variable = variable || {};
|
variable = variable || {};
|
||||||
@ -167,104 +128,12 @@ export class TemplateSrv implements BaseTemplateSrv {
|
|||||||
args = [];
|
args = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (format) {
|
const formatItem = formatRegistry.getIfExists(format);
|
||||||
case 'regex': {
|
if (!formatItem) {
|
||||||
if (typeof value === 'string') {
|
throw new Error(`Variable format ${format} not found`);
|
||||||
return kbn.regexEscape(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const escapedValues = _.map(value, kbn.regexEscape);
|
|
||||||
if (escapedValues.length === 1) {
|
|
||||||
return escapedValues[0];
|
|
||||||
}
|
|
||||||
return '(' + escapedValues.join('|') + ')';
|
|
||||||
}
|
|
||||||
case 'lucene': {
|
|
||||||
return this.luceneFormat(value);
|
|
||||||
}
|
|
||||||
case 'pipe': {
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return value.join('|');
|
|
||||||
}
|
|
||||||
case 'distributed': {
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return this.distributeVariable(value, variable.name);
|
|
||||||
}
|
|
||||||
case 'csv': {
|
|
||||||
if (_.isArray(value)) {
|
|
||||||
return value.join(',');
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
case 'html': {
|
|
||||||
if (_.isArray(value)) {
|
|
||||||
return textUtil.escapeHtml(value.join(', '));
|
|
||||||
}
|
|
||||||
return textUtil.escapeHtml(value);
|
|
||||||
}
|
|
||||||
case 'json': {
|
|
||||||
return JSON.stringify(value);
|
|
||||||
}
|
|
||||||
case 'percentencode': {
|
|
||||||
// like glob, but url escaped
|
|
||||||
if (_.isArray(value)) {
|
|
||||||
return this.encodeURIComponentStrict('{' + value.join(',') + '}');
|
|
||||||
}
|
|
||||||
return this.encodeURIComponentStrict(value);
|
|
||||||
}
|
|
||||||
case 'singlequote': {
|
|
||||||
// escape single quotes with backslash
|
|
||||||
const regExp = new RegExp(`'`, 'g');
|
|
||||||
if (_.isArray(value)) {
|
|
||||||
return _.map(value, v => `'${_.replace(v, regExp, `\\'`)}'`).join(',');
|
|
||||||
}
|
|
||||||
return `'${_.replace(value, regExp, `\\'`)}'`;
|
|
||||||
}
|
|
||||||
case 'doublequote': {
|
|
||||||
// escape double quotes with backslash
|
|
||||||
const regExp = new RegExp('"', 'g');
|
|
||||||
if (_.isArray(value)) {
|
|
||||||
return _.map(value, v => `"${_.replace(v, regExp, '\\"')}"`).join(',');
|
|
||||||
}
|
|
||||||
return `"${_.replace(value, regExp, '\\"')}"`;
|
|
||||||
}
|
|
||||||
case 'sqlstring': {
|
|
||||||
// escape single quotes by pairing them
|
|
||||||
const regExp = new RegExp(`'`, 'g');
|
|
||||||
if (_.isArray(value)) {
|
|
||||||
return _.map(value, v => `'${_.replace(v, regExp, "''")}'`).join(',');
|
|
||||||
}
|
|
||||||
return `'${_.replace(value, regExp, "''")}'`;
|
|
||||||
}
|
|
||||||
case 'date': {
|
|
||||||
return this.formatDate(value, args);
|
|
||||||
}
|
|
||||||
case 'glob': {
|
|
||||||
if (_.isArray(value) && value.length > 1) {
|
|
||||||
return '{' + value.join(',') + '}';
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
formatDate(value: any, args: string[]): string {
|
return formatItem.formatter(value, args, variable);
|
||||||
const arg = args[0] ?? 'iso';
|
|
||||||
|
|
||||||
switch (arg) {
|
|
||||||
case 'ms':
|
|
||||||
return value;
|
|
||||||
case 'seconds':
|
|
||||||
return `${Math.round(parseInt(value, 10)! / 1000)}`;
|
|
||||||
case 'iso':
|
|
||||||
return dateTime(parseInt(value, 10)).toISOString();
|
|
||||||
default:
|
|
||||||
return dateTime(parseInt(value, 10)).format(arg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setGrafanaVariable(name: string, value: any) {
|
setGrafanaVariable(name: string, value: any) {
|
||||||
@ -310,7 +179,7 @@ export class TemplateSrv implements BaseTemplateSrv {
|
|||||||
str = _.escape(str);
|
str = _.escape(str);
|
||||||
this.regex.lastIndex = 0;
|
this.regex.lastIndex = 0;
|
||||||
return str.replace(this.regex, (match, var1, var2, fmt2, var3) => {
|
return str.replace(this.regex, (match, var1, var2, fmt2, var3) => {
|
||||||
if (this.getVariableAtIndex(var1 || var2 || var3) || this.builtIns[var1 || var2 || var3]) {
|
if (this.getVariableAtIndex(var1 || var2 || var3)) {
|
||||||
return '<span class="template-variable">' + match + '</span>';
|
return '<span class="template-variable">' + match + '</span>';
|
||||||
}
|
}
|
||||||
return match;
|
return match;
|
||||||
@ -448,17 +317,6 @@ export class TemplateSrv implements BaseTemplateSrv {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
distributeVariable(value: any, variable: any) {
|
|
||||||
value = _.map(value, (val: any, index: number) => {
|
|
||||||
if (index !== 0) {
|
|
||||||
return variable + '=' + val;
|
|
||||||
} else {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return value.join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
private getVariableAtIndex(name: string) {
|
private getVariableAtIndex(name: string) {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
<query-editor-row
|
<query-editor-row query-ctrl="ctrl" can-collapse="false">
|
||||||
query-ctrl="ctrl"
|
|
||||||
can-collapse="false"
|
|
||||||
>
|
|
||||||
<div class="gf-form-inline">
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<label class="gf-form-label query-keyword width-9">Service</label>
|
<label class="gf-form-label query-keyword width-9">Service</label>
|
||||||
@ -143,7 +140,9 @@
|
|||||||
<label class="gf-form-label query-keyword width-9">Dimension</label>
|
<label class="gf-form-label query-keyword width-9">Dimension</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<a ng-click="ctrl.azureMonitorAddDimensionFilter()" class="gf-form-label query-part"><icon name="'plus'"></icon></a>
|
<a ng-click="ctrl.azureMonitorAddDimensionFilter()" class="gf-form-label query-part"
|
||||||
|
><icon name="'plus'"></icon
|
||||||
|
></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form gf-form--grow">
|
<div class="gf-form gf-form--grow">
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
@ -175,10 +174,14 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<a ng-click="ctrl.azureMonitorRemoveDimensionFilter($index)" class="gf-form-label query-part"><icon name="'minus'"></icon></a>
|
<a ng-click="ctrl.azureMonitorRemoveDimensionFilter($index)" class="gf-form-label query-part"
|
||||||
|
><icon name="'minus'"></icon
|
||||||
|
></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form" ng-if="$last">
|
<div class="gf-form" ng-if="$last">
|
||||||
<a ng-click="ctrl.azureMonitorAddDimensionFilter()" class="gf-form-label query-part"><icon name="'plus'"></icon></a>
|
<a ng-click="ctrl.azureMonitorAddDimensionFilter()" class="gf-form-label query-part"
|
||||||
|
><icon name="'plus'"></icon
|
||||||
|
></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form gf-form--grow">
|
<div class="gf-form gf-form--grow">
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
@ -332,17 +335,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-if="ctrl.target.queryType === 'Insights Analytics'">
|
<div ng-if="ctrl.target.queryType === 'Insights Analytics'">
|
||||||
<div class="gf-form gf-form--grow">
|
<div class="gf-form gf-form--grow">
|
||||||
<kusto-editor
|
<kusto-editor
|
||||||
class="gf-form gf-form--grow"
|
class="gf-form gf-form--grow"
|
||||||
query="ctrl.target.insightsAnalytics.query"
|
query="ctrl.target.insightsAnalytics.query"
|
||||||
placeholder="'Application Insights Query'"
|
placeholder="'Application Insights Query'"
|
||||||
change="ctrl.onInsightsAnalyticsQueryChange"
|
change="ctrl.onInsightsAnalyticsQueryChange"
|
||||||
execute="ctrl.onQueryExecute"
|
execute="ctrl.onQueryExecute"
|
||||||
variables="ctrl.templateVariables"
|
variables="ctrl.templateVariables"
|
||||||
getSchema="ctrl.getAppInsightsQuerySchema"
|
getSchema="ctrl.getAppInsightsQuerySchema"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-inline">
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
@ -360,141 +363,140 @@
|
|||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-if="ctrl.target.queryType === 'Application Insights'">
|
<div ng-if="ctrl.target.queryType === 'Application Insights'">
|
||||||
<div class="gf-form-inline">
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<label class="gf-form-label query-keyword width-9">Metric</label>
|
<label class="gf-form-label query-keyword width-9">Metric</label>
|
||||||
<gf-form-dropdown
|
<gf-form-dropdown
|
||||||
model="ctrl.target.appInsights.metricName"
|
model="ctrl.target.appInsights.metricName"
|
||||||
allow-custom="true"
|
allow-custom="true"
|
||||||
lookup-text="true"
|
lookup-text="true"
|
||||||
get-options="ctrl.getAppInsightsMetricNames($query)"
|
get-options="ctrl.getAppInsightsMetricNames($query)"
|
||||||
on-change="ctrl.onAppInsightsMetricNameChange()"
|
on-change="ctrl.onAppInsightsMetricNameChange()"
|
||||||
css-class="min-width-20"
|
css-class="min-width-20"
|
||||||
>
|
|
||||||
</gf-form-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label query-keyword width-9">Aggregation</label>
|
|
||||||
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
|
|
||||||
<select
|
|
||||||
class="gf-form-input"
|
|
||||||
ng-model="ctrl.target.appInsights.aggregation"
|
|
||||||
ng-options="f as f for f in ctrl.target.appInsights.aggOptions"
|
|
||||||
ng-change="ctrl.refresh()"
|
|
||||||
></select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form gf-form--grow">
|
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form-inline">
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label query-keyword width-9">Group By</label>
|
|
||||||
</div>
|
|
||||||
<div ng-repeat="d in ctrl.target.appInsights.dimension track by $index"
|
|
||||||
class="gf-form"
|
|
||||||
ng-click="ctrl.removeGroupBy($index);"
|
|
||||||
onmouseover="this.style['text-decoration'] = 'line-through';"
|
|
||||||
onmouseout="this.style['text-decoration'] = '';">
|
|
||||||
<label class="gf-form-label"
|
|
||||||
style="cursor: pointer;">{{d}} <icon name="'times'"></icon></label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<gf-form-dropdown
|
|
||||||
allow-custom="true"
|
|
||||||
lookup-text="true"
|
|
||||||
placeholder="Add"
|
|
||||||
model="ctrl.dummyDiminsionString"
|
|
||||||
get-options="ctrl.getAppInsightsGroupBySegments($query)"
|
|
||||||
on-change="ctrl.getAppInsightsGroupBySegments"
|
|
||||||
css-class="min-width-5"
|
|
||||||
>
|
|
||||||
</gf-form-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form-inline">
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label query-keyword">Filter</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="gf-form-input width-17"
|
|
||||||
ng-model="ctrl.target.appInsights.dimensionFilter"
|
|
||||||
spellcheck="false"
|
|
||||||
placeholder="your/groupby eq 'a_value'"
|
|
||||||
ng-blur="ctrl.refresh()"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form gf-form--grow">
|
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form-inline">
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label query-keyword width-9">Time Grain</label>
|
|
||||||
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
|
|
||||||
<select
|
|
||||||
class="gf-form-input"
|
|
||||||
ng-model="ctrl.target.appInsights.timeGrainType"
|
|
||||||
ng-options="f as f for f in ['auto', 'none', 'specific']"
|
|
||||||
ng-change="ctrl.updateTimeGrainType()"
|
|
||||||
></select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="gf-form"
|
|
||||||
ng-hide="ctrl.target.appInsights.timeGrainType === 'auto' || ctrl.target.appInsights.timeGrainType === 'none'"
|
|
||||||
>
|
>
|
||||||
|
</gf-form-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-9">Aggregation</label>
|
||||||
|
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
|
||||||
|
<select
|
||||||
|
class="gf-form-input"
|
||||||
|
ng-model="ctrl.target.appInsights.aggregation"
|
||||||
|
ng-options="f as f for f in ctrl.target.appInsights.aggOptions"
|
||||||
|
ng-change="ctrl.refresh()"
|
||||||
|
></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-9">Group By</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ng-repeat="d in ctrl.target.appInsights.dimension track by $index"
|
||||||
|
class="gf-form"
|
||||||
|
ng-click="ctrl.removeGroupBy($index);"
|
||||||
|
onmouseover="this.style['text-decoration'] = 'line-through';"
|
||||||
|
onmouseout="this.style['text-decoration'] = '';"
|
||||||
|
>
|
||||||
|
<label class="gf-form-label" style="cursor: pointer;">{{d}} <icon name="'times'"></icon></label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<gf-form-dropdown
|
||||||
|
allow-custom="true"
|
||||||
|
lookup-text="true"
|
||||||
|
placeholder="Add"
|
||||||
|
model="ctrl.dummyDiminsionString"
|
||||||
|
get-options="ctrl.getAppInsightsGroupBySegments($query)"
|
||||||
|
on-change="ctrl.getAppInsightsGroupBySegments"
|
||||||
|
css-class="min-width-5"
|
||||||
|
>
|
||||||
|
</gf-form-dropdown>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword">Filter</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="gf-form-input width-3"
|
class="gf-form-input width-17"
|
||||||
ng-model="ctrl.target.appInsights.timeGrainCount"
|
ng-model="ctrl.target.appInsights.dimensionFilter"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
placeholder=""
|
placeholder="your/groupby eq 'a_value'"
|
||||||
ng-blur="ctrl.updateAppInsightsTimeGrain()"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="gf-form"
|
|
||||||
ng-hide="ctrl.target.appInsights.timeGrainType === 'auto' || ctrl.target.appInsights.timeGrainType === 'none'"
|
|
||||||
>
|
|
||||||
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent timegrainunit-dropdown-wrapper">
|
|
||||||
<select
|
|
||||||
class="gf-form-input"
|
|
||||||
ng-model="ctrl.target.appInsights.timeGrainUnit"
|
|
||||||
ng-options="f as f for f in ['minute', 'hour', 'day', 'month', 'year']"
|
|
||||||
ng-change="ctrl.updateAppInsightsTimeGrain()"
|
|
||||||
></select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form" ng-hide="ctrl.target.appInsights.timeGrainType !== 'auto'">
|
|
||||||
<label class="gf-form-label">Auto Interval</label>
|
|
||||||
<label class="gf-form-label">{{ctrl.getAppInsightsAutoInterval()}}</label>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form gf-form--grow">
|
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form-inline">
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label query-keyword width-9">Legend Format</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="gf-form-input width-30"
|
|
||||||
ng-model="ctrl.target.appInsights.alias"
|
|
||||||
spellcheck="false"
|
|
||||||
placeholder="alias patterns (see help for more info)"
|
|
||||||
ng-blur="ctrl.refresh()"
|
ng-blur="ctrl.refresh()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form gf-form--grow">
|
</div>
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-9">Time Grain</label>
|
||||||
|
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
|
||||||
|
<select
|
||||||
|
class="gf-form-input"
|
||||||
|
ng-model="ctrl.target.appInsights.timeGrainType"
|
||||||
|
ng-options="f as f for f in ['auto', 'none', 'specific']"
|
||||||
|
ng-change="ctrl.updateTimeGrainType()"
|
||||||
|
></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="gf-form"
|
||||||
|
ng-hide="ctrl.target.appInsights.timeGrainType === 'auto' || ctrl.target.appInsights.timeGrainType === 'none'"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="gf-form-input width-3"
|
||||||
|
ng-model="ctrl.target.appInsights.timeGrainCount"
|
||||||
|
spellcheck="false"
|
||||||
|
placeholder=""
|
||||||
|
ng-blur="ctrl.updateAppInsightsTimeGrain()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="gf-form"
|
||||||
|
ng-hide="ctrl.target.appInsights.timeGrainType === 'auto' || ctrl.target.appInsights.timeGrainType === 'none'"
|
||||||
|
>
|
||||||
|
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent timegrainunit-dropdown-wrapper">
|
||||||
|
<select
|
||||||
|
class="gf-form-input"
|
||||||
|
ng-model="ctrl.target.appInsights.timeGrainUnit"
|
||||||
|
ng-options="f as f for f in ['minute', 'hour', 'day', 'month', 'year']"
|
||||||
|
ng-change="ctrl.updateAppInsightsTimeGrain()"
|
||||||
|
></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form" ng-hide="ctrl.target.appInsights.timeGrainType !== 'auto'">
|
||||||
|
<label class="gf-form-label">Auto Interval (see query options)</label>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form-inline">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-9">Legend Format</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="gf-form-input width-30"
|
||||||
|
ng-model="ctrl.target.appInsights.alias"
|
||||||
|
spellcheck="false"
|
||||||
|
placeholder="alias patterns (see help for more info)"
|
||||||
|
ng-blur="ctrl.refresh()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form" ng-show="ctrl.lastQueryError">
|
<div class="gf-form" ng-show="ctrl.lastQueryError">
|
||||||
|
@ -499,7 +499,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
generateAutoUnits(timeGrain: string, timeGrains: Array<{ value: string }>) {
|
generateAutoUnits(timeGrain: string, timeGrains: Array<{ value: string }>) {
|
||||||
if (timeGrain === 'auto') {
|
if (timeGrain === 'auto') {
|
||||||
return TimegrainConverter.findClosestTimeGrain(
|
return TimegrainConverter.findClosestTimeGrain(
|
||||||
this.templateSrv.getBuiltInIntervalValue(),
|
'1m',
|
||||||
_.map(timeGrains, o => TimegrainConverter.createKbnUnitFromISO8601Duration(o.value)) || [
|
_.map(timeGrains, o => TimegrainConverter.createKbnUnitFromISO8601Duration(o.value)) || [
|
||||||
'1m',
|
'1m',
|
||||||
'5m',
|
'5m',
|
||||||
@ -580,16 +580,6 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
return this.templateSrv.getVariables().map(t => '$' + t.name);
|
return this.templateSrv.getVariables().map(t => '$' + t.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Application Insights Section */
|
|
||||||
|
|
||||||
getAppInsightsAutoInterval() {
|
|
||||||
const interval = this.templateSrv.getBuiltInIntervalValue();
|
|
||||||
if (interval[interval.length - 1] === 's') {
|
|
||||||
return '1m';
|
|
||||||
}
|
|
||||||
return interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAppInsightsMetricNames() {
|
getAppInsightsMetricNames() {
|
||||||
if (!this.datasource.appInsightsDatasource.isConfigured()) {
|
if (!this.datasource.appInsightsDatasource.isConfigured()) {
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user