mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
[Azure monitor] Support variables for Azure Resource Group subscription picker (#38620)
This commit is contained in:
parent
f567bef0bf
commit
f1529b83a2
@ -20,7 +20,7 @@ import { Observable, from } from 'rxjs';
|
||||
import { mergeMap } from 'rxjs/operators';
|
||||
import { getAuthType, getAzureCloud, getAzurePortalUrl } from '../credentials';
|
||||
import { isGUIDish } from '../components/ResourcePicker/utils';
|
||||
import { routeNames } from '../utils/common';
|
||||
import { interpolateVariable, routeNames } from '../utils/common';
|
||||
|
||||
interface AdhocQuery {
|
||||
datasourceId: number;
|
||||
@ -118,7 +118,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
workspace = this.firstWorkspace;
|
||||
}
|
||||
|
||||
const query = templateSrv.replace(item.query, scopedVars, this.interpolateVariable);
|
||||
const query = templateSrv.replace(item.query, scopedVars, interpolateVariable);
|
||||
|
||||
return {
|
||||
refId: target.refId,
|
||||
@ -272,7 +272,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
|
||||
private buildQuery(query: string, options: any, workspace: string): AdhocQuery[] {
|
||||
const querystringBuilder = new LogAnalyticsQuerystringBuilder(
|
||||
getTemplateSrv().replace(query, {}, this.interpolateVariable),
|
||||
getTemplateSrv().replace(query, {}, interpolateVariable),
|
||||
options,
|
||||
'TimeGenerated'
|
||||
);
|
||||
@ -293,29 +293,6 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
return queries;
|
||||
}
|
||||
|
||||
interpolateVariable(value: string, variable: { multi: any; includeAll: any }) {
|
||||
if (typeof value === 'string') {
|
||||
if (variable.multi || variable.includeAll) {
|
||||
return "'" + value + "'";
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
const quotedValues = map(value, (val) => {
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
return "'" + val + "'";
|
||||
});
|
||||
return quotedValues.join(',');
|
||||
}
|
||||
|
||||
async getDefaultOrFirstSubscription(): Promise<string | undefined> {
|
||||
if (this.defaultSubscriptionId) {
|
||||
return this.defaultSubscriptionId;
|
||||
|
@ -2,9 +2,6 @@ import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
import AzureResourceGraphDatasource from './azure_resource_graph_datasource';
|
||||
import { CustomVariableModel, initialVariableModelState, VariableHide } from 'app/features/variables/types';
|
||||
import { initialCustomVariableModelState } from 'app/features/variables/custom/reducer';
|
||||
|
||||
const templateSrv = new TemplateSrv();
|
||||
|
||||
const single: CustomVariableModel = {
|
||||
...initialVariableModelState,
|
||||
@ -38,7 +35,30 @@ const multi: CustomVariableModel = {
|
||||
type: 'custom',
|
||||
};
|
||||
|
||||
templateSrv.init([single, multi]);
|
||||
const subs: CustomVariableModel = {
|
||||
...initialVariableModelState,
|
||||
id: 'subs',
|
||||
name: 'subs',
|
||||
index: 3,
|
||||
current: { value: ['sub-foo', 'sub-baz'], text: 'sub-foo + sub-baz', selected: true },
|
||||
options: [
|
||||
{ selected: true, value: 'sub-foo', text: 'sub-foo' },
|
||||
{ selected: false, value: 'sub-bar', text: 'sub-bar' },
|
||||
{ selected: true, value: 'sub-baz', text: 'sub-baz' },
|
||||
],
|
||||
multi: true,
|
||||
includeAll: false,
|
||||
query: '',
|
||||
hide: VariableHide.dontHide,
|
||||
type: 'custom',
|
||||
};
|
||||
|
||||
const templateSrv = new TemplateSrv({
|
||||
getVariables: () => [subs, single, multi],
|
||||
getVariableWithName: jest.fn(),
|
||||
getFilteredVariables: jest.fn(),
|
||||
});
|
||||
templateSrv.init([subs, single, multi]);
|
||||
|
||||
jest.mock('app/core/services/backend_srv');
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
@ -77,7 +97,7 @@ describe('AzureResourceGraphDatasource', () => {
|
||||
azureResourceGraph: { query: 'Resources | var1-foo', resultFormat: 'table' },
|
||||
queryType: 'Azure Resource Graph',
|
||||
refId: undefined,
|
||||
subscriptions: undefined,
|
||||
subscriptions: [],
|
||||
});
|
||||
});
|
||||
|
||||
@ -95,53 +115,27 @@ describe('AzureResourceGraphDatasource', () => {
|
||||
},
|
||||
queryType: 'Azure Resource Graph',
|
||||
refId: undefined,
|
||||
subscriptions: undefined,
|
||||
subscriptions: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('When interpolating variables', () => {
|
||||
beforeEach(() => {
|
||||
ctx.variable = { ...initialCustomVariableModelState };
|
||||
});
|
||||
|
||||
describe('and value is a string', () => {
|
||||
it('should return an unquoted value', () => {
|
||||
expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual('abc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and value is a number', () => {
|
||||
it('should return an unquoted value', () => {
|
||||
expect(ctx.ds.interpolateVariable(1000, ctx.variable)).toEqual(1000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and value is an array of strings', () => {
|
||||
it('should return comma separated quoted values', () => {
|
||||
expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).toEqual("'a','b','c'");
|
||||
});
|
||||
});
|
||||
|
||||
describe('and variable allows multi-value and value is a string', () => {
|
||||
it('should return a quoted value', () => {
|
||||
ctx.variable.multi = true;
|
||||
expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'");
|
||||
});
|
||||
});
|
||||
|
||||
describe('and variable contains single quote', () => {
|
||||
it('should return a quoted value', () => {
|
||||
ctx.variable.multi = true;
|
||||
expect(ctx.ds.interpolateVariable("a'bc", ctx.variable)).toEqual("'a'bc'");
|
||||
});
|
||||
});
|
||||
|
||||
describe('and variable allows all and value is a string', () => {
|
||||
it('should return a quoted value', () => {
|
||||
ctx.variable.includeAll = true;
|
||||
expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'");
|
||||
});
|
||||
it('should apply subscription variable', () => {
|
||||
const target = {
|
||||
subscriptions: ['$subs'],
|
||||
azureResourceGraph: {
|
||||
query: 'resources | where $__contains(name, $var3)',
|
||||
resultFormat: '',
|
||||
},
|
||||
};
|
||||
expect(ctx.ds.applyTemplateVariables(target)).toStrictEqual({
|
||||
azureResourceGraph: {
|
||||
query: `resources | where $__contains(name, 'var3-foo','var3-baz')`,
|
||||
resultFormat: 'table',
|
||||
},
|
||||
queryType: 'Azure Resource Graph',
|
||||
refId: undefined,
|
||||
subscriptions: ['sub-foo', 'sub-baz'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -3,6 +3,7 @@ import _ from 'lodash';
|
||||
import { AzureMonitorQuery, AzureDataSourceJsonData, AzureQueryType } from '../types';
|
||||
import { ScopedVars } from '@grafana/data';
|
||||
import { getTemplateSrv, DataSourceWithBackend } from '@grafana/runtime';
|
||||
import { interpolateVariable } from '../utils/common';
|
||||
|
||||
export default class AzureResourceGraphDatasource extends DataSourceWithBackend<
|
||||
AzureMonitorQuery,
|
||||
@ -19,39 +20,26 @@ export default class AzureResourceGraphDatasource extends DataSourceWithBackend<
|
||||
}
|
||||
|
||||
const templateSrv = getTemplateSrv();
|
||||
const query = templateSrv.replace(item.query, scopedVars, this.interpolateVariable);
|
||||
const variableNames = templateSrv.getVariables().map((v) => `$${v.name}`);
|
||||
const subscriptionVar = _.find(target.subscriptions, (sub) => _.includes(variableNames, sub));
|
||||
const interpolatedSubscriptions = templateSrv
|
||||
.replace(subscriptionVar, scopedVars, (v: any) => v)
|
||||
.split(',')
|
||||
.filter((v) => v.length > 0);
|
||||
const subscriptions = [
|
||||
...interpolatedSubscriptions,
|
||||
..._.filter(target.subscriptions, (sub) => !_.includes(variableNames, sub)),
|
||||
];
|
||||
const query = templateSrv.replace(item.query, scopedVars, interpolateVariable);
|
||||
|
||||
return {
|
||||
refId: target.refId,
|
||||
queryType: AzureQueryType.AzureResourceGraph,
|
||||
subscriptions: target.subscriptions,
|
||||
subscriptions,
|
||||
azureResourceGraph: {
|
||||
resultFormat: 'table',
|
||||
query,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
interpolateVariable(value: string, variable: { multi: any; includeAll: any }) {
|
||||
if (typeof value === 'string') {
|
||||
if (variable.multi || variable.includeAll) {
|
||||
return "'" + value + "'";
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
const quotedValues = _.map(value, (val) => {
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
return "'" + val + "'";
|
||||
});
|
||||
return quotedValues.join(',');
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ const SubscriptionField: React.FC<SubscriptionFieldProps> = ({
|
||||
<MultiSelect
|
||||
menuShouldPortal
|
||||
isClearable
|
||||
value={findOptions(subscriptions, query.subscriptions)}
|
||||
value={findOptions([...subscriptions, ...variableOptionGroup.options], query.subscriptions)}
|
||||
inputId="azure-monitor-subscriptions-field"
|
||||
onChange={onSubscriptionsChange}
|
||||
options={options}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { hasOption } from './common';
|
||||
import { initialCustomVariableModelState } from 'app/features/variables/custom/reducer';
|
||||
import { hasOption, interpolateVariable } from './common';
|
||||
|
||||
describe('AzureMonitor: hasOption', () => {
|
||||
it('can find an option in flat array', () => {
|
||||
@ -40,3 +41,44 @@ describe('AzureMonitor: hasOption', () => {
|
||||
expect(hasOption(options, 'c-b')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('When interpolating variables', () => {
|
||||
describe('and value is a string', () => {
|
||||
it('should return an unquoted value', () => {
|
||||
expect(interpolateVariable('abc', initialCustomVariableModelState)).toEqual('abc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and value is a number', () => {
|
||||
it('should return an unquoted value', () => {
|
||||
expect(interpolateVariable(1000, initialCustomVariableModelState)).toEqual(1000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and value is an array of strings', () => {
|
||||
it('should return comma separated quoted values', () => {
|
||||
expect(interpolateVariable(['a', 'b', 'c'], initialCustomVariableModelState)).toEqual("'a','b','c'");
|
||||
});
|
||||
});
|
||||
|
||||
describe('and variable allows multi-value and value is a string', () => {
|
||||
it('should return a quoted value', () => {
|
||||
const variable = { ...initialCustomVariableModelState, multi: true };
|
||||
expect(interpolateVariable('abc', variable)).toEqual("'abc'");
|
||||
});
|
||||
});
|
||||
|
||||
describe('and variable contains single quote', () => {
|
||||
it('should return a quoted value', () => {
|
||||
const variable = { ...initialCustomVariableModelState, multi: true };
|
||||
expect(interpolateVariable("a'bc", variable)).toEqual("'a'bc'");
|
||||
});
|
||||
});
|
||||
|
||||
describe('and variable allows all and value is a string', () => {
|
||||
it('should return a quoted value', () => {
|
||||
const variable = { ...initialCustomVariableModelState, includeAll: true };
|
||||
expect(interpolateVariable('abc', variable)).toEqual("'abc'");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { map } from 'lodash';
|
||||
import { rangeUtil } from '@grafana/data';
|
||||
import TimegrainConverter from '../time_grain_converter';
|
||||
import { AzureMonitorOption } from '../types';
|
||||
@ -36,3 +37,26 @@ export const routeNames = {
|
||||
appInsights: 'appinsights',
|
||||
resourceGraph: 'resourcegraph',
|
||||
};
|
||||
|
||||
export function interpolateVariable(value: any, variable: { multi: any; includeAll: any }) {
|
||||
if (typeof value === 'string') {
|
||||
if (variable.multi || variable.includeAll) {
|
||||
return "'" + value + "'";
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
const quotedValues = map(value, (val) => {
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
return "'" + val + "'";
|
||||
});
|
||||
return quotedValues.join(',');
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user